开发者

Generating XML file from SQL Server 2008

I am working on an application where I need to get the SQL response as XML into an XML file (and to store it in some physical location, say c:\xyz.xml).

I am able to generate the XML content using the provisions available in SQL Server as shown below.

 SELECT * FROM @T开发者_StackOverflow社区able FOR XML AUTO, ELEMENTS

where: @Table is a table variable.

I want to know how I can store the query output to an XML file from SQL Server itself.


There's one more option - use sqlcmd tool.

  1. Add :XML ON as a first line in your SQL file (let's call it input.sql)
  2. A command like this will do the trick:
sqlcmd -S <your-server> -i input.sql -o output.xml


You need to use xp_cmdshell, and the bcp utility in the following way

EXEC xp_cmdshell 'bcp "SELECT * FROM @Table FOR XML AUTO, ELEMENTS" queryout "C:\table.xml" -c -T'

Hit me back in the comments if you've got any questions or want to know anything more about how this works.


You can't write to the file system from SQL Server itself. At least not that easily. There are three alternatives:

  • use xp_cmdshell. I would strongly advise against it. By default xp_cmdshell is disabled for security purposes, and to have it enabled it just for this operation opens a way to big security hole in your system.

  • use the FileSystemObject and the OLE Automation procedures sp_OACreate/sp_OAMethod. See Reading and Writing Files in SQL Server using T-SQL. This, while marginally better than the xp_cmdshell option, it doesn't give a much better security story. The only reason why is better than xp_cmdshell is that is by far less known by hackers. But the OLE Automation procedures option in SQL Server is also disabled by default and enabling this option exposes the very same security problems xp_cmdshell has.

  • use a CLR procedure. This would be my recommendation. Create an assembly with digital signature, use the assembly signature to allow, through Transact-SQL code signing, EXTERNAL ACCESS, then use the CLR procedure to write the XML into the file system. While this is significantly more complex than the simple xp_cmdshell or OLE Automation options, it is the most controlable and granular one from a security point of view and is the easiest to maintain and get right (is .Net code as opposed to shell scripts). Unfortunately, by default the clr option is also disabled in the server and has to be enabled.


If you press

ctrl + shift + f

you will have selected "Results To File." This can be found in the Query menu on the top bar of Sql Management Studio.

Or put something like this into your sql script

exec xp_cmdshell 'bcp "select * from suppliers" queryout "c:\suppliers.txt" -S server -T'

See this link, there is an issue about whether it is the app's c drive or the sql server's c drive. Have fun sorting that out.


You can create CLR function that create the file, build it into the sql server, and use it from a stored procedure

Another way( I haven't tested it ) - There is a tool bcp

bcp "Select * from dbo..table FOR XML RAW" queryout c:\temp\test.xml -Soc-db -Uuser -Ppassword 

This example is from here


Simple SQL Write to File method

DECLARE @xml XML = '<MyXML></MyXMl>'
DECLARE @strXML varchar(max) = convert(varchar(max),@XML) 

-- Add white space for readability 
SELECT @strxml = replace(@strxml,'</',char(13) + char(10) + '</')
--- Add Declartives, namespaces and xls 

Create Table dbo.BCP_OUT(contents varchar(max)) 
INSERT INTO dbo.bcp_out(contents)
SELECT Convert(varchar(max),@strXML ) 

EXEC xp_cmdshell N'BCP -S SERVER [database].dbo.bcp_out -T -c -o \\pathto\file.name' 


If your xml output is relatively small (<4000 characters), then you can use this SP:

IF EXISTS (SELECT TOP 1 1 FROM sys.objects WHERE object_id = OBJECT_ID('dbo.USP_WRITE_UNICODE_STRING_TO_FILE') AND type = 'P')
BEGIN
    DROP PROCEDURE dbo.USP_WRITE_UNICODE_STRING_TO_FILE
END
GO

-- =============================================
-- Description: Writes the specified Unicode string to the specified file.
-- Permissions: This stored procedure uses xp_cmdshell which is disabled by default. To enable it:
--              1. In Management Studio connect to a component of SQL Server.
--              2. In Object Explorer, right-click the server, and then click Facets.
--              3. In the View Facets dialog box, expand the Facet list, and select the Surface Area Configuration.
--              4. In the Facet properties area, select XPCmdShellEnabled property and set its value to True.
--              5. Click OK.
-- Example:     EXEC dbo.USP_WRITE_UNICODE_STRING_TO_FILE'<root> <a b="c" /> </root>', 'C:\Test.xml', 1;
-- =============================================
CREATE PROCEDURE dbo.USP_WRITE_UNICODE_STRING_TO_FILE
(
    @Str            NVARCHAR(4000),
    @XmlFilePath    NVARCHAR(256),
    @Debug          BIT = 0
)
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @Str1 NVARCHAR(MAX),
            @Cmd NVARCHAR(4000),
            @MaxLen int = 4000;

    --see: http://technet.microsoft.com/en-us/library/bb490897.aspx
    SET @Str1 = REPLACE(REPLACE(REPLACE(@Str, '>', '^>'), '<', '^<'), '"', '^"');
    -- '>' Writes the command output to a file 
    SET @Str1 =N'ECHO ' + @Str1  + N'>"'+ @XmlFilePath + N'"';
    IF @Debug = 1
    BEGIN
        DECLARE @Msg varchar(128) = 'The total lenght is ' + CAST(LEN(@Str1) AS VARCHAR(10)) + ' characters.'
        PRINT @Msg;
        PRINT @Str1;
    END

    IF (LEN(@Str1) > @MaxLen)
        RAISERROR  ('The input string is too long', 11, 0);
    ELSE
        SET @Cmd = CAST (@Str1 AS NVARCHAR(4000));

    EXEC master..xp_cmdshell @Cmd, NO_OUTPUT;           
END
GO

--Test 1
DECLARE @Str NVARCHAR(4000);
DECLARE @Xml xml = '<root> <a b="c" /> </root>';
SET @Str = CAST (@Xml AS NVARCHAR(4000));
EXEC dbo.USP_WRITE_UNICODE_STRING_TO_FILE @Str, 'C:\Test.xml', 1;
GO
--Test 2
DECLARE @Str NVARCHAR(4000);
SET @Str = REPLICATE('a', 4000);
EXEC dbo.USP_WRITE_UNICODE_STRING_TO_FILE @Str, 'C:\Test.xml', 1;
GO

If you don't work with Unicode, then you can create another SP: USP_WRITE_NON_UNICODE_STRING_TO_FILE, which will be very similar to the previous one with the following changes:

    CREATE PROCEDURE dbo.USP_WRITE_NON_UNICODE_STRING_TO_FILE
    (
        @Str            VARCHAR(8000),
        @XmlFilePath    NVARCHAR(256),
        @Debug          BIT = 0
    )
    AS
    BEGIN
        SET NOCOUNT ON;

        DECLARE @Str1 VARCHAR(MAX),
                @Cmd VARCHAR(8000),
                @MaxLen int = 8000;
        ...
        SET @Cmd = CAST (@Str1 AS VARCHAR(8000));

That SP allows the use of two times longer the input string (<8000 characters).

If your XML is longer than 8000 but less than 1MB you can use sqlcmd utility without :XML ON command. It greatly simplify the usage of the utility because you don't need a separate input_file with :XML ON command included. Here is an example:

DECLARE @Cmd NVARCHAR(4000);
SET @Cmd = N'sqlcmd -S ' + @@SERVERNAME + N' -d ' + DB_NAME() + 
N' -Q "SET NOCOUNT ON; DECLARE @Xml xml = ''<root> <a >b</a> </root>''; SELECT CONVERT(NVARCHAR(MAX), @Xml);" -o "C:\Test.xml" -y 0';
PRINT @Cmd;
EXEC master..xp_cmdshell @Cmd, NO_OUTPUT;

You can also use an SP here:

CREATE PROCEDURE dbo.USP_SAMPLE_PROCEDURE
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @Xml xml;
    SET @Xml = (SELECT name, type_desc FROM sys.objects FOR XML PATH('object'), ROOT('sys.objects'));
    SELECT CONVERT(NVARCHAR(MAX), @Xml)
END
GO

DECLARE @Cmd NVARCHAR(4000);
SET @Cmd = N'sqlcmd -S ' + @@SERVERNAME + N' -d ' + DB_NAME() + 
N' -Q "EXEC dbo.USP_SAMPLE_PROCEDURE;" -o "C:\Test.xml" -y 0';
PRINT @Cmd;
EXEC master..xp_cmdshell @Cmd, NO_OUTPUT;
GO

If your XML is more than 1MB you should use :XML ON command in a separate script and specify it as -i input_file parameter.


I made this SP so I can easily extract data from db or temp table to XML file on file system. It supports where clause.

CREATE PROCEDURE dbo.ExportToXMLFile
      @TableName varchar(1000)
    , @Where varchar(2000)=''
    , @TicketNumber varchar(500)
    , @debug bit=0
as
/*
    Date:2016-03-27
    Author: BojNed
    Purpose: Exports data from table to XML file on filesystem. 
    @TableName = name of table to export. 
    @Where = optitional, to set @Where Clause. DO NOT ENTER WHERE at beggining of the string 
    @TicketNumber = To save to folder on filesystem
    @Debug = Optitional. To debug this SP.

    Examples:
    EXEC dbo.ExportToXMLFile '#tt','columnX=2','221',0
    EXEC dbo.ExportToXMLFile '[Production].[Product]','','252',1
    EXEC dbo.ExportToXMLFile '[dbo].[DatabaseLog]','ColumnZ=55','351',0
    EXEC dbo.ExportToXMLFile '[dbo].[DatabaseLog]','','7865',1
*/
begin 

    if @debug=0
        SET NOCOUNT ON

    declare @SQL nvarchar(max)
    declare @IsTempTable bit
    declare @NewTableName varchar(1000)
    declare @Xml as XML

    if (isnull(@TicketNumber,''))=''
    begin
        RAISERROR('No ticket number defined',16,1,1)
        RETURN
    END

    --check if table is tmp or variable
    if (SELECT SUBSTRING(@TableName,1,1))='#' or (SELECT SUBSTRING(@TableName,1,1))='@'
    BEGIN
        if @debug=1
            PRINT 'Source is TMP table'
        set @NewTableName='TMPTBL_'+@TableName
    END
    ELSE
    BEGIN
        if @debug=1
            PRINT 'Source is db table'
        set @NewTableName=replace(@TableName,'.','_')
    END

        --RemoveSpecialChars
        declare @KeepValues varchar(1000)
        set @KeepValues = '%[^a-z^0-9^_]%'
        WHILE PATINDEX(@KeepValues,@NewTableName)>0
        set @NewTableName = STUFF(@NewTableName, PATINDEX(@KeepValues,@NewTableName),1,'')

    if @debug=1
        PRINT 'Node name for XML Header and filename: '+@NewTableName

    if ISNULL(@Where,'')=''
    BEGIN
        set @SQL= 'SELECT * FROM '+ @TableName+' FOR XML PATH, ROOT ('''+@NewTableName+'''), ELEMENTS'
        if @debug=1
            PRINT 'NO Where condition'
    END
    ELSE
    BEGIN
        set @SQL= 'SELECT * FROM '+ @TableName+' WHERE '+@Where+ ' FOR XML PATH, ROOT ('''+@NewTableName+'''), ELEMENTS'
        if @debug=1
            PRINT 'With Where condition'
    END

    --Get XML to tbl
    if ISNULL(OBJECT_ID ('tempdb..##TXML'),0)>0
    DROP TABLE ##TXML

    CREATE TABLE ##TXML (XMLText XML)

    set @SQL =  ' insert into ##TXML select ('+@SQL+')'

    --parse query
    declare @testsql nvarchar(max)
    declare @result int
    set @testsql = N'set parseonly on; ' + @sql
    exec @result = sp_executesql @testsql

    -- If it worked, execute it
    if @result = 0
        begin
            if @debug=1
                PRINT 'Query OK: '+ @SQL
            exec sp_executesql @sql
        end
    else 
    BEGIN
        DECLARE @msg varchar(2000)
        set @msg ='Parsing Error on query: ' + @SQL
        RAISERROR (@msg,16,1,1)
        RETURN
    END

    DECLARE @Tbl TABLE (id int identity(1,1), Dir varchar(256))

    --check if dir exsists
    INSERT into @Tbl
    EXEC master.dbo.xp_subdirs 'C:\DataCorrectionBackup\'


    if (SELECT Count(*) from @Tbl WHERE Dir=@TicketNumber)=0
    BEGIN
        --create new dir
        DECLARE @t varchar(500)
        set @t ='C:\DataCorrectionBackup\'+@TicketNumber

        EXEC master.sys.xp_create_subdir @t
    END

    declare @bcp varchar(500)
    declare @Filename VARCHAR(255)

    set @Filename =convert(varchar(100),GETDATE(),112)+'_'+replace(convert(varchar(100),GETDATE(),114),':','')+'_'+@NewTableName+'.xml'

    set @bcp = 'bcp "SELECT XMLText from ##TXML" queryout C:\DataCorrectionBackup\'+@TicketNumber+'\'+@Filename+' -w -T -S'+ @@servername

    --save file
    if @debug=0
        EXEC xp_cmdshell @bcp, NO_OUTPUT
    ELSE 
    BEGIN
        EXEC xp_cmdshell @bcp
        PRINT @bcp
    END

    DROP table ##TXML

end

go
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜