Using BCP in a SQL query - sql-server

I have a stored procedure that updates several columns in a table and then selects out those columns so I can save them out for a report I run. Currently I am doing the exporting and saving manually though by setting column size in 'Query Options' to 333, turning off all the other options, putting the results to text and then saving the file out with the name and format. I know that there must be a way to automate this though the export and save process though.
I looked into using a job with Vb script, but this is a simple report that is run on demand and not on a schedule; so a job seemed to be overkill. The other option I found was BCP, which looks like what I need. I only know of this as a CMD tool though and can't quite figure out how to leverage this within a SQL query. In case it is needed here is an example of what my stored procedure is doing. Any help is appreciated and thank you in advance. I am using SQL 2008 R2 btw.
UPDATE Table#1 SET Column1 = '' WHERE Column1 = 'XX'
UPDATE Table#1 SET Column2 = '' WHERE Column2 = '000000'
SELECT Column1 +
Column2 +
Column3
FROM Table#1 Where Column4 = 'T'

BCP is a command line utility, however it can be access through SQL with the use of XP Command Shell so you will need xp_cmdshell enabled. The following is a parameterized example of how to call this inside of SQL
DECLARE #Server varchar(50) = 'ServerName' /* Source SQL Server */
DECLARE #FileName varchar(50) = ''
DECLARE #SourceProc varchar(50) = ''
DECLARE #sql varchar(8000)
DECLARE #RunDate varchar(10) = REPLACE(CONVERT(date, getdate()), '-','')
DECLARE #FilePath as varchar(255) = '\\UNCPathtoOutputFile\'
DECLARE #StartDate as Date = (select dateadd(month,datediff(month,0,GETDATE())-2,0))--'1/1/2013' /* Example of required Date as parameter */
DECLARE #EndDate as Date = (SELECT dateadd(month,datediff(month,0,GETDATE())-0,-1))--'2/1/2013' /* Example of required Date as parameter */
-- BCP Export
SET #SourceProc = '[Schema].[StoredProcName]'
SET #FileName = 'SomeOutputFile' + #RunDate + '.txt'
SET #FilePath = #FilePath + #FileName
SET #sql = 'bcp "EXEC [Database].' + #SourceProc + ' ''' + CONVERT(varchar(10),#StartDate, 101) + ''',''' + CONVERT(varchar(10),#EndDate, 101) + '''" QUERYOUT "' + #FilePath + '" -c -t"|" -r\n -S' + #Server + ' -T'
--PRINT(#SQL)
exec master.dbo.xp_cmdshell #sql

Related

Issue with Dynamic SQL (Also: How to get SQL Lines from ExecuteNonQuery)

I am using Visual Studio, I'm trying to make a Insert statement using C# and Dynamic SQL.
SQL Command:
comando.CommandText = #"
declare #sql nvarchar(max);
set #sql = 'INSERT INTO ' + #curso + '(Data_Local, Deletado) VALUES ( ' + #date + ', 0)';
exec (#sql);
";
#curso is the Table Name, it's working, Data_Local is a Datetime value, which is not working, and Deletado is a bit value, which is working.
Declaring #curso:
comando.Parameters.Add("#curso", SqlDbType.NVarChar);
comando.Parameters["#curso"].Value = idCurso;
Declaring #date
comando.Parameters.Add("#date", SqlDbType.DateTime);
comando.Parameters["#date"].Value = dateTimePicker2.Value.Date;
Then after:
comando.ExecuteNonQuery();
The error message appears:
'Conversion failed when converting date and/or time from character
string.'
What should I do to read the value from the DateTimePicker, insert into the #date variable, and add it to the SQL Command properly?
Also, I have another doubt, where can I see how the SQL Command is sent? I need to see how the lines really are, how is the SQL Command without the variables, with the values instead.
Sorry if my issue is not explained very well, I'll keep trying to make it clear.
Edit:
I tried:
comando.CommandText = #"
declare #sql nvarchar(max);
set #sql = 'INSERT INTO ' + #curso + '(Data_Local, Deletado) VALUES ( ' + #date + ', 0)';
exec (#sql);
";
comando.Parameters.Add("#date", SqlDbType.NVarChar);
comando.Parameters["#date"].Value = "'" + dateTimePicker2.Value.Date + "'";
New error message:
'The conversion of a varchar data type to a datetime data type
resulted in an out-of-range value. The statement has been terminated.'
Edit. Thanks, solved.
Rather use a Stored Procedure, its awful if you going to use dynamic SQL in your c# application. Firstly because as a DBA, Adhoc queries like these are difficult to maintain, and secondly its easier to manage and also easier to apply to code.
1) Create a Stored Procedure
CREATE PROCEDURE Insert_Test
(
#curso VARCHAR(256),
#date VARCHAR(32)
)
AS
SET NOCOUNT ON
DECLARE #sql NVARCHAR(MAX)
IF ISDATE(#date) = 1 --check if its the correct date
BEGIN
SET #sql = 'INSERT INTO ' + #curso + '(Data_Local, Deletado) VALUES ( ''' + #date + ''', 0)'
EXEC (#sql)
END
2) In your c# code
string tbleName = "Test";
DateTime dte = DateTime.Now.ToString();
comando.ExecuteSqlCommand("exec Insert_Test #curso, #date",
new SqlParameter("curso", tbleName),
new SqlParameter("date", dte)
);

BCP export to XML, opening XML gives error: Extra content at the end of the document

I´m trying to export to XML format using BCP and the XML file is generated correctly but the actual content seems wrong. Can anyone help please?
When I try to open the XML in a browser I get the following error message:
This page contains the following errors: error on line 1 at column 62:
Extra content at the end of the document
The SQL select that I'm using is:
DECLARE #fileName VARCHAR(50)
DECLARE #sqlStr VARCHAR(1000)
DECLARE #sqlCmd VARCHAR(1000)
SET #fileName = 'c:\fund_lib\test.xml'
USE PORT_APP_SQL
DROP TABLE ##temp;
WITH cte1
AS (SELECT LTRIM(RTRIM(codigo)) AS code,
CONVERT(VARCHAR(10), fecha, 120) AS date,
precio AS NAV
FROM mpr_price_history
WHERE codigo IN( 'LU0038743380', 'LU0086913042', 'LU0265291665', 'LU0098860363',
'LU0128525689', 'LU0121204944', 'CZ0008474780', 'LU0363630376',
'LU0372180066', 'LU0271663857', 'LU0271663774', 'LU0363630707', 'LU0313643024' ))
SELECT *
INTO ##temp
FROM cte1
SET #sqlStr = 'select * from ##temp order by code, date desc FOR XML RAW (''code'');'
-- Save XML records to a file:
SET #sqlCmd = 'bcp "' + #sqlStr + '" queryout ' + #fileName
+ ' -S "MPR01\SQLEXPRESS" -T -w'
EXEC xp_cmdshell #sqlCmd
And this is the error message if I open it in Firefox ( sorry is in Spanish).
error message seems to be at the end of each line
I think that your whole query can be simplified... No need for a CTE or a temp table...
attention The solution for your problem is - probably! - the missing root node, as mentioned by #TT.. If adding a root node solves your problem, please do not accept my answer (although you might vote it up, if you like it :-) ).
Your problem might be bound to a mix of encodings. If your output includes special characters, there could be some problems when you mix 8-byte encoding (VARCHAR) and the output with option -w. Therefore I put this all in NVARCHAR(MAX)
My suggestion to get things slim:
USE PORT_APP_SQL;
DECLARE #fileName NVARCHAR(50) = 'c:\fund_lib\test.xml';
DECLARE #cmd NVARCHAR(MAX);
SET #cmd =
N'SELECT LTRIM(RTRIM(codigo)) AS code
,CONVERT(VARCHAR(10), fecha, 120) AS [date]
,precio AS NAV
FROM mpr_price_history
WHERE codigo IN( ''LU0038743380'', ''LU0086913042'', ''LU0265291665'', ''LU0098860363'',
''LU0128525689'', ''LU0121204944'', ''CZ0008474780'', ''LU0363630376'',
''LU0372180066'', ''LU0271663857'', ''LU0271663774'', ''LU0363630707'', ''LU0313643024'' )
ORDER BY code,[date] DESC
FOR XML RAW(''code''),ROOT(''root'');'
-- Save XML records to a file:
SET #cmd = N'bcp "' + #cmd + N'" queryout ' + #fileName
+ N' -S "MPR01\SQLEXPRESS" -T -w'
EXEC xp_cmdshell #cmd;
The reason is because the XML doesn't have a root path. This example based on your script should produce XML for which the browser doesn't complain:
DECLARE #fileName VARCHAR(50);
DECLARE #sqlStr VARCHAR(1000);
DECLARE #sqlCmd VARCHAR(1000);
SET #fileName = 'c:\temp\test.xml';
SELECT *
INTO ##temp
FROM (VALUES('LU0038743380',GETDATE(),1),
('LU0086913042',GETDATE(),2),
('LU0265291665',GETDATE(),3),
('LU0098860363',GETDATE(),4)) AS cte1(fecha,[date],nav);
SET #sqlStr = 'select (select * from ##temp FOR XML RAW(''code''),TYPE) FOR XML PATH(''data'');'
-- Save XML records to a file:
SET #sqlCmd = 'bcp "' + #sqlStr + '" queryout ' + #fileName
+ ' -S '+##SERVERNAME+' -T -w';
EXEC xp_cmdshell #sqlCmd;
DROP TABLE ##temp;
I have ran your code with small alterations with no errors.
It seems the issue is with your data.
Look for special XML characters.

How to do multiple select in#SqlStr when using BCP

I have code like this run with no error and I got result.
DECLARE #fileName VARCHAR(50)
DECLARE #sqlStr VARCHAR(1000)
DECLARE #sqlCmd VARCHAR(1000)
SET #fileName = 'C:\SQL_Queries\test.xml'
SET #sqlStr = 'select * from ##tmpEmployeeJob FOR XML PATH(''EmployeeJob''), ROOT(''Pos.EmployeeJob'')'
SET #sqlCmd = 'bcp "' + #sqlStr + '" queryout ' + #fileName + ' -w -T'
EXEC xp_cmdshell #sqlCmd
But when I want to do multiple select in sqlstr like this
SET #sqlStr = 'select(select * from ##tmpEmployeeJob FOR XML PATH(''EmployeeJob''), ROOT(''Pos.EmployeeJob''),
select * from ##tmpCity FOR XML PATH(''City''), ROOT(''Pos.City''))'
I got this error
Incorrect syntax near 'EmployeeJob'
How can I do the multiple select like above?
When you exporting xml you can easily compose xml strings from two or more queries. Or you can export table with more rows (where rows can be xml data type).

SQL Server output to a file using variable name

I want to send SQL Server output to a file which is working as expected. But if I pass the file path in a variable, It is not creating the file.
Working
:out 'C:\Temp.txt'
Not Working
DECLARE #BCVFileName VARCHAR(500)
SET #BCVFileName= 'C:\Temp.txt'
:out #BCVFileName
Could anyone please help me on this??
Thanks,
Mahesh
For this you need to store your query in Query file and then you can execute from command to store result to a text file as shown below.
>sqlcmd -E -S(Local)\SQLExpress -YourDBName -iC:\myQuery -oC:\Output.txt
Another way to use T-SQL and create a batch statement to execute via XP_CMDSHELL. Following script will help you to do the same just replace your query, ServerName with used variables.
SET NOCOUNT ON;
GO
DECLARE #sqlcmd varchar(1000);
PRINT 'using SqlCMD.exe';
SET #sqlcmd = 'sqlcmd -S' + ##SERVERNAME + ' -E -oc:\outfile.txt '
DECLARE #cmd varchar(1000);
SET #cmd = '-Q"SELECT RIGHT(REPLICATE('' '' , 6) + CONVERT(varchar, AddressID), 6 ) AS AddressID, CONVERT(varchar(10), ModifiedDate, 121) AS ModifiedDate FROM AdventureWorks.Person.Address ORDER BY ModifiedDate;"';
SET #sqlcmd = #sqlcmd + #cmd;
--SELECT #sqlcmd;
EXEC xp_cmdshell #sqlcmd, no_output;
PRINT 'using BCP.exe';
SET #cmd = '"SELECT RIGHT(REPLICATE('' '' , 6) + CONVERT(varchar, AddressID), 6 ) AS AddressID, CONVERT(varchar(10), ModifiedDate, 121) AS ModifiedDate FROM AdventureWorks.Person.Address ORDER BY ModifiedDate;"';
SET #sqlcmd = 'bcp ' + #cmd + ' queryout "c:\outfile2.txt" -c -T -S' + ##SERVERNAME
--SELECT #sqlcmd;
EXEC xp_cmdshell #sqlcmd, no_output;
regards

Exporting stored procedure result to excel with column name

I have attached the code for storing the stored procedure output to an Excel sheet..
but I have few issues, they are
(1) all the columns are displayed in the first column of the excel sheet..which I don't need, I need to show the report in diff columns.. how to do that..
(2) if I use inner join with the main, how can I get the column names of the other table
Can anyone please help me out to solve the above issues?
alter procedure proc_generate_excel_with_columns
(
#db_name varchar(100),
#schm_name varchar(100),
#table_name varchar(100),
#file_name varchar(100)
)
as
--Generate column names as a recordset
declare #columns varchar(8000), #sql varchar(8000), #data_file varchar(100)
select
#columns = coalesce(#columns+',', '') + column_name + ' as ' + column_name
from
information_schema.columns
where
table_name = #table_name
select
#columns = '''' + replace(replace(#columns, ' as ', ''' as '),',',',''')
--Create a dummy file to have actual data
select
#data_file = substring(#file_name, 1, len(#file_name) -
charindex('\', reverse(#file_name))) + '\data_file.xls'
--Generate column names in the passed EXCEL file
set #sql='bcp " select * from (select ' + #columns + ') as t" queryout
c:\test.xls -c -t, -T -S ' + ##servername
exec master..xp_cmdshell #sql
--Generate data in the dummy file
set #sql = 'bcp "select * from st..VEN_FULLREPORTMASTER
where entry_date = convert(varchar, getdate() - 3, 105) "
queryout c:\data_file.xls -c -t, -T -S' + ##servername
-- exec master..xp_cmdshell #sql
-- Copy dummy file to passed EXCEL file
set #sql = 'exec master..xp_cmdshell ''type '+#data_file+' >> "'+#file_name+'"'''
exec(#sql)
----Delete dummy file
set #sql = 'exec master..xp_cmdshell ''del ' + #data_file + ''''
exec(#sql)
If using SSIS is an option this would be a trivial task to complete using a data flow task.
Can you use SSIS or do you need a pure tSQL solution?

Resources