Delimiters with bcp import - sql

I am trying to import bulk data from text files. The statement that i use is below:
DECLARE #s as varchar(300)
SET #s='bcp [hc]..[QRYM_COMPANIES] in ' + char(34) + 'C:\SQL Files\health canada allfiles\comp.txt' + char(34) + ' -C ACP -c -t \", -r\n -e' + char(34) + 'C:\SQL Files\health canada allfiles\err\comp.err.txt' + char(34) + ' -b250 -m50 -SServer -Usa -P' + char(34) + 'mypassword' + char(34) + ' -h' + char(34) + 'TABLOCK' + char(34)
PRINT #s
EXEC master..xp_cmdshell #s
Now, normally I would have put -t, but my data contains commas in some of the data fields which results in the data being shifted. When I try to use -t \" I get the error "String data, right truncation", so that is why I am having to use the combination of -t ,\"
The problem here is that I get quotations in my transferred data. Below is a sample row of my data:
"20042","NSL95","4797","NU SKIN INTERNATIONAL, INC.","DIN OWNER","Y","N","N","N","","75 WEST CENTER STREET","PROVO","UTAH","UNITED STATES","84601",""
The table description is below. To avoid the "string truncation error" i have added a 1 to the left field size.
Can someone help me fix this problem?
Thanks
CREATE TABLE [health_canada].[dbo].[QRYM_COMPANIES] (
DRUG_CODE VARCHAR(18) NOT NULL, --NUMERIC(8) NOT NULL,
MFR_CODE VARCHAR(15),
COMPANY_CODE VARCHAR(16), --NUMERIC(6),
COMPANY_NAME VARCHAR(180),
COMPANY_TYPE VARCHAR(140),
ADDRESS_MAILING_FLAG VARCHAR(11),
ADDRESS_BILLING_FLAG VARCHAR(11),
ADDRESS_NOTIFICATION_FLAG VARCHAR(11),
ADDRESS_OTHER VARCHAR(11),
SUITE_NUMERIC VARCHAR(120),
STREET_NAME VARCHAR(180),
CITY_NAME VARCHAR(160),
PROVINCE VARCHAR(140),
COUNTRY VARCHAR(140),
POSTAL_CODE VARCHAR(120),
POST_OFFICE_BOX VARCHAR(115)
) ON [PRIMARY]
GO

Consider to use a format file with your bcp call. It is easy to configure, and you can fine tune it for your file. Just add -f and the path to use it: http://msdn.microsoft.com/en-us/library/ms162802.aspx
Steps
Add the format file to the disk (make sure that the SQL server can reach the path).
Update paths in query.
Run query.
I used this file:
This code imported your example data without quotations with the format file:
DECLARE #importTable VARCHAR(128) = 'hc.dbo.QRYM_COMPANIES';
DECLARE #importPath VARCHAR(8000) = '\\YOURPATH\comp.txt';
DECLARE #formatFilePath VARCHAR(8000) = '\\YOURPATH\formatFile.txt';
DECLARE #separator VARCHAR(10) = '';
DECLARE #export VARCHAR(8000) = 'bcp "' + #importTable + '" in "' + #importPath + '" -T -f "' + #formatFilePath + '" -C RAW -S ' + ##SERVERNAME;
PRINT(#export)
EXEC xp_cmdshell #export
Output:
SELECT * FROM hc.dbo.QRYM_COMPANIES:
Add this format file to your disk:
<?xml version="1.0"?>
<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RECORD>
<FIELD ID="0" xsi:type="CharTerm" TERMINATOR='"' MAX_LENGTH="1" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="1" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="18" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="2" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="15" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="3" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="16" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="4" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="180" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="5" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="140" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="6" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="11" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="7" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="11" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="8" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="11" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="9" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="11" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="10" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="120" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="11" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="180" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="12" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="160" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="13" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="140" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="14" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="140" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="15" xsi:type="CharTerm" TERMINATOR='","' MAX_LENGTH="120" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="16" xsi:type="CharTerm" TERMINATOR='"\r\n' MAX_LENGTH="115" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
</RECORD>
<ROW>
<COLUMN SOURCE="1" NAME="DRUG_CODE" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="2" NAME="MFR_CODE" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="3" NAME="COMPANY_CODE" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="4" NAME="COMPANY_NAME" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="5" NAME="COMPANY_TYPE" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="6" NAME="ADDRESS_MAILING_FLAG" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="7" NAME="ADDRESS_BILLING_FLAG" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="8" NAME="ADDRESS_NOTIFICATION_FLAG" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="9" NAME="ADDRESS_OTHER" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="10" NAME="SUITE_NUMERIC" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="11" NAME="STREET_NAME" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="12" NAME="CITY_NAME" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="13" NAME="PROVINCE" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="14" NAME="COUNTRY" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="15" NAME="POSTAL_CODE" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="16" NAME="POST_OFFICE_BOX" xsi:type="SQLVARYCHAR"/>
</ROW>
</BCPFORMAT>
You can auto generate new format files with bcp if you need it for other files/tables (no need to do this now if the format file works)
Important:
After format file export:
Replace: TERMINATOR="\"," to TERMINATOR='","'
Replace: TERMINATOR="\r\n" to TERMINATOR='"\r\n'*
Add to <record> (will remove first quotation): <FIELD ID="0" xsi:type="CharTerm" TERMINATOR='"' MAX_LENGTH="1" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
Auto generate query:
DECLARE #importTable VARCHAR(128) = 'hc.dbo.QRYM_COMPANIES';
DECLARE #formatFilePath VARCHAR(8000) = '\\YourFilePath\formatFile.txt';
DECLARE #bcpCall VARCHAR(8000);
SET #bcpCall = 'bcp "' + #importTable + '" format nul -c -t", -x -f "' + #formatFilePath + '" -T -S ' + ##SERVERNAME;
PRINT #bcpCall
EXEC xp_cmdshell #bcpCall

Related

Update 3rd level deep field value in an XML using xQuery

I have the following sample XML and I am looking to Update:
AttendeeID to 7878 (will be a sql variable) where sequence = 1 (will be a sql variable)
Expected output is to see 7878 in the AttendeeID field's value. When I run any of the 2 options I tried, it does not yield the correct result. For Eg. Delete works but the element is not added. Replace value does not update the value.
Any inputs are highly appreciated. Thank you.
--------XML ---------------------------
DECLARE #cartXML XML =
'<OBJECT CLASS="Test1" ID="-1" FULL="FULL" VERSION="1">
<FIELD NAME="OrderDate">20220619</FIELD>
<FIELD NAME="OrderParty">Individual</FIELD>
<FIELD NAME="ShipToID">34567</FIELD>
<FIELD NAME="ShipToAddress1">123 Test Street</FIELD>
<FIELD NAME="ShipToCity">TestCity</FIELD>
<FIELD NAME="ShipToState">IL</FIELD>
<FIELD NAME="ShipTocountry">USA</FIELD>
<FIELD NAME="TaxNumber">444</FIELD>
<FIELD NAME="DiscountCode">Summer22</FIELD>
<SUBTYPE NAME="SubType1">
<OBJECT NAME="SubType111" ID="-1">
<FIELD NAME="TestID">-1</FIELD>
<FIELD NAME="Sequence">1</FIELD>
<FIELD NAME="ParentSequence">-1</FIELD>
<FIELD NAME="ExtID">-1</FIELD>
<FIELD NAME="ExtName">ABC</FIELD>
</OBJECT>
<OBJECT NAME="SubType111" ID="-1">
<FIELD NAME="TestID">-1</FIELD>
<FIELD NAME="Sequence">2</FIELD>
<FIELD NAME="ParentSequence">1</FIELD>
<FIELD NAME="ExtID">-1</FIELD>
<FIELD NAME="ExtName">DEF</FIELD>
<FIELD NAME="__ExtendedData"><OBJECT
CLASS="Meet123" ID="-1" FULL="FULL"
VERSION="1"><FIELD
NAME="OrderDetailID">-1</FIELD><FIELD
NAME="OrderID">-1</FIELD><FIELD
NAME="Sequence">0</FIELD><FIELD
NAME="AttendeeID">123</FIELD><FIELD NAME="AttendeeID_Name">Test, Mark/I
H 6</FIELD><FIELD
NAME="ShowList">1</FIELD><FIELD
NAME="BdgeName">Mark</FIELD><FIELD
NAME="BadgeCompanyName">I H 6</FIELD>
</OBJECT></FIELD>
</OBJECT>
<OBJECT NAME="SubType111" ID="-1">
<FIELD NAME="TestID">-1</FIELD>
<FIELD NAME="Sequence">3</FIELD>
<FIELD NAME="ParentSequence">1</FIELD>
<FIELD NAME="ExtID">-1</FIELD>
<FIELD NAME="ExtName">GHI</FIELD>
</OBJECT>
</SUBTYPE>
<SUBTYPE NAME="SubType2"/>
<SUBTYPE NAME="SubType3"/>
</OBJECT>';
-----------------------SQL -----------------------
select #cartXML as originalXML
DECLARE #ID as int ,#productID as int, #attendeeId as int = 7878,
#sequenceId as int, #orderLineXML as XML , #ExtendedAttrDetail as XML
SET #sequenceId = 2
select #orderlineXML = c.query('.'), #ExtendedAttrDetail = w.query('.') from
#cartXML.nodes('/OBJECT/SUBTYPE/OBJECT[FIELD[#NAME="Sequence"]/text()=sql:variable("#sequenceId")]') t1(c)
Cross APPLY (VALUES(TRY_CAST(c.query('FIELD[#NAME="__ExtendedData"]').value('.','NVARCHAR(MAX)') AS XML)))AS t2(w)
-----This works..But I am looking to alter #cartXML as it contains the entire XML
SET #ExtendedAttrDetail.modify('replace value of
(/OBJECT/FIELD[#NAME="AttendeeID"]/text())[1]
with sql:variable("#attendeeId")')
--select #ExtendedAttrDetail
------- Option 1( Preferred)---does not work--
SET #cartXML.modify ('replace value of
(/OBJECT/SUBTYPE/OBJECT[FIELD[#NAME="Sequence"]/text()=sql:variable("#sequenceId")]/FIELD[#NAME="__ExtendedData"]/OBJECT/FIELD[#NAME="AttendeeID"]/text())[1] with sql:variable("#attendeeId")')
select #cartXML as ModifiedDirectly
---Option 2 (Insert does not add correctly )
--SET #cartXML.modify('delete
--/OBJECT/SUBTYPE/OBJECT[FIELD[#NAME="Sequence"]/text()=sql:variable("#sequenceId")]
--/FIELD[#NAME="__ExtendedData"]');
--SET #cartXML.modify('insert sql:variable("#ExtendedAttrDetail") into
--(/OBJECT/SUBTYPE/OBJECT[FIELD[#NAME="Sequence"]/text()=sql:variable("#sequenceId")])
--[1]');
--SELECT #cartXML as UpdatedXL;
Please try the following solution.
The issue is that the XML fragment in question is encoded.
SQL
DECLARE #cartXML XML =
N'<OBJECT CLASS="Test1" ID="-1" FULL="FULL" VERSION="1">
<FIELD NAME="OrderDate">20220619</FIELD>
<FIELD NAME="OrderParty">Individual</FIELD>
<FIELD NAME="ShipToID">34567</FIELD>
<FIELD NAME="ShipToAddress1">123 Test Street</FIELD>
<FIELD NAME="ShipToCity">TestCity</FIELD>
<FIELD NAME="ShipToState">IL</FIELD>
<FIELD NAME="ShipTocountry">USA</FIELD>
<FIELD NAME="TaxNumber">444</FIELD>
<FIELD NAME="DiscountCode">Summer22</FIELD>
<SUBTYPE NAME="SubType1">
<OBJECT NAME="SubType111" ID="-1">
<FIELD NAME="TestID">-1</FIELD>
<FIELD NAME="Sequence">1</FIELD>
<FIELD NAME="ParentSequence">-1</FIELD>
<FIELD NAME="ExtID">-1</FIELD>
<FIELD NAME="ExtName">ABC</FIELD>
</OBJECT>
<OBJECT NAME="SubType111" ID="-1">
<FIELD NAME="TestID">-1</FIELD>
<FIELD NAME="Sequence">2</FIELD>
<FIELD NAME="ParentSequence">1</FIELD>
<FIELD NAME="ExtID">-1</FIELD>
<FIELD NAME="ExtName">DEF</FIELD>
<FIELD NAME="__ExtendedData"><OBJECT
CLASS="Meet123" ID="-1" FULL="FULL"
VERSION="1"><FIELD
NAME="OrderDetailID">-1</FIELD><FIELD
NAME="OrderID">-1</FIELD><FIELD
NAME="Sequence">0</FIELD><FIELD
NAME="AttendeeID">123</FIELD><FIELD NAME="AttendeeID_Name">Test, Mark/I
H 6</FIELD><FIELD
NAME="ShowList">1</FIELD><FIELD
NAME="BdgeName">Mark</FIELD><FIELD
NAME="BadgeCompanyName">I H 6</FIELD>
</OBJECT></FIELD>
</OBJECT>
<OBJECT NAME="SubType111" ID="-1">
<FIELD NAME="TestID">-1</FIELD>
<FIELD NAME="Sequence">3</FIELD>
<FIELD NAME="ParentSequence">1</FIELD>
<FIELD NAME="ExtID">-1</FIELD>
<FIELD NAME="ExtName">GHI</FIELD>
</OBJECT>
</SUBTYPE>
<SUBTYPE NAME="SubType2"/>
<SUBTYPE NAME="SubType3"/>
</OBJECT>';
DECLARE #ExtendedData XML
, #attendeeId INT = 770;;
-- Step #1: select XML fragment in question as real XML data type
SELECT #ExtendedData = w
FROM #cartxml.nodes('/OBJECT/SUBTYPE/OBJECT[#ID="-1"]') as t1(c)
CROSS APPLY (VALUES(TRY_CAST(c.query('FIELD[#NAME="__ExtendedData"]').value('.','NVARCHAR(MAX)') AS XML))) AS t2(w)
WHERE w.exist('/OBJECT[#CLASS="Meet123"]') = 1;
-- Step #2: remove encoded XML fragment
SET #cartXML.modify('replace value of (/OBJECT/SUBTYPE[#NAME="SubType1"]/OBJECT/FIELD[#NAME="__ExtendedData"]/text())[1]
with ""');
-- Step #3: modify AttendeeID
SET #ExtendedData.modify('replace value of
(/OBJECT/FIELD[#NAME="AttendeeID"]/text())[1]
with sql:variable("#attendeeId")');
-- Step #4: insert real XML fragment
SET #cartXML.modify('insert sql:variable("#ExtendedData") into
(/OBJECT/SUBTYPE[#NAME="SubType1"]/OBJECT/FIELD[#NAME="__ExtendedData"])[1]');
-- test
SELECT #cartXML;

Convert nested XML via XQuery to flat xml

I've a nested piece of XML i want to insert in to SQL.
Import XML:
<RECORD>
<RECID>118810</RECID>
<FIELD TYPE="C">
<NAME>proj_code</NAME>
<VALUE>118810</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>sub_nr</NAME>
<VALUE>99900</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>proj_desc</NAME>
<VALUE>Nagekomen kosten Oktober 2018</VALUE>
</FIELD>
<FIELD TYPE="N">
<NAME>pro_stat</NAME>
<VALUE>9</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>comment</NAME>
<VALUE></VALUE>
</FIELD>
</RECORD>
I want to convert via XQuery it into:
<RECORD>
<RECID>118810</RECID>
<proj_code>118810</proj_code>
<sub_nr>99900</sub_nr>
<proj_desc>Nagekomen kosten Oktober 2018</proj_desc>
<pro_stat>9</pro_stat>
<comment></comment>
</RECORD>
so i can import it into SQL.
Any thoughts?
As Martin Honnen pointed out, MS SQL Server XQuery doesn't support computed dynamic element names, just literals. Unfortunately, including even the latest SQL Server 2019. Here is an ugly solution.
SQL
DECLARE #xml XML = N'<root>
<RECORD>
<RECID>118810</RECID>
<FIELD TYPE="C">
<NAME>proj_code</NAME>
<VALUE>118810</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>sub_nr</NAME>
<VALUE>99900</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>proj_desc</NAME>
<VALUE>Nagekomen kosten Oktober 2018</VALUE>
</FIELD>
<FIELD TYPE="N">
<NAME>pro_stat</NAME>
<VALUE>9</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>comment</NAME>
<VALUE></VALUE>
</FIELD>
</RECORD>
<RECORD>
<RECID>118811</RECID>
<FIELD TYPE="C">
<NAME>proj_code</NAME>
<VALUE>118811</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>sub_nr</NAME>
<VALUE>99901</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>proj_desc</NAME>
<VALUE>Nagekomen kosten November 2019</VALUE>
</FIELD>
<FIELD TYPE="N">
<NAME>pro_stat</NAME>
<VALUE>19</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>comment</NAME>
<VALUE>wow</VALUE>
</FIELD>
</RECORD>
</root>';
DECLARE #tbl TABLE (ID INT IDENTITY(1,1) PRIMARY KEY, RECID VARCHAR(10), [col_name] VARCHAR(20), [col_value] VARCHAR(100));
INSERT INTO #tbl
SELECT c.value('(../RECID/text())[1]', 'VARCHAR(100)') AS [RECID]
, c.value('(NAME/text())[1]', 'VARCHAR(30)') AS [name]
, c.value('(VALUE/text())[1]', 'VARCHAR(100)') AS [value]
FROM #xml.nodes('root/RECORD/FIELD') AS t(c);
DECLARE #RowCount INT = (SELECT MAX(ID) FROM #tbl)
, #recID varchar(10) = (SELECT TOP(1) RECID FROM #tbl WHERE ID = 1)
, #xml_data VARCHAR(MAX) = '<root><RECORD>';
WHILE #RowCount > 0 BEGIN
SELECT #xml_data += IIF(#recID != RECID, '</RECORD><RECORD>', '') +
--'<' + [col_Name] + '>' + COALESCE([col_value],'') + '</' + [col_Name] + '>'
CONCAT('<',[col_Name],'>',[col_value],'</',[col_Name],'>')
, #recID = RECID
FROM #tbl
ORDER BY ID DESC OFFSET #RowCount - 1 ROWS FETCH NEXT 1 ROWS ONLY;
SET #RowCount -= 1;
END;
SET #xml_data += '</RECORD></root>';
SELECT CAST(#xml_data AS XML);
Simply map each RECORD to a new one where you map all FIELDs to elements with the name from NAME and the value from VALUE:
RECORD !
<RECORD>
{
RECID,
FIELD ! element { NAME } { data(VALUE) }
}
</RECORD>
https://xqueryfiddle.liberty-development.net/nbUY4kB
For XQuery 1 you would need to use for return instead of the map operator !:
for $record in //RECORD
return
<RECORD>
{
$record/RECID,
for $field in $record/FIELD
return element { $field/NAME } { data($field/VALUE) }
}
</RECORD>
https://xqueryfiddle.liberty-development.net/nbUY4kB/1
I tried this:
DECLARE #xml xml;
SET #xml =N'
<RECORD>
<RECID>118810</RECID>
<FIELD TYPE="C">
<NAME>proj_code</NAME>
<VALUE>118810</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>sub_nr</NAME>
<VALUE>99900</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>proj_desc</NAME>
<VALUE>Nagekomen kosten Oktober 2018</VALUE>
</FIELD>
<FIELD TYPE="N">
<NAME>pro_stat</NAME>
<VALUE>9</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>comment</NAME>
<VALUE></VALUE>
</FIELD>
</RECORD>
<RECORD>
<RECID>118811</RECID>
<FIELD TYPE="C">
<NAME>proj_code</NAME>
<VALUE>118811</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>sub_nr</NAME>
<VALUE>99900</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>proj_desc</NAME>
<VALUE>Nagekomen kosten November 2018</VALUE>
</FIELD>
<FIELD TYPE="N">
<NAME>pro_stat</NAME>
<VALUE>9</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>comment</NAME>
<VALUE></VALUE>
</FIELD>
</RECORD>
';
SELECT #xml.query('
for $record in //RECORD
return
<RECORD>
{
$record/RECID,
for $field in $record/FIELD
return element { $field/NAME } { data($field/VALUE) }
}
</RECORD>
' ) as result
But i get an error on the return element part:
XQuery [query()]: Only constant expressions are supported for the name expression of computed element and attribute constructors.
This is the final version with select for inserting it into SQL:
DECLARE #xml XML = N'
<AVXML>
<SIGNONMSGRS>
<DTSERVER>2019-09-10T15:54:32</DTSERVER>
<APPID>ACCOUNTVIEW</APPID>
<APPVER>0908-</APPVER>
</SIGNONMSGRS>
<EBUSMSGSRS>
<EBUSQRYRS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RECORD>
<RECID>118810</RECID>
<FIELD TYPE="C">
<NAME>proj_code</NAME>
<VALUE>118810</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>sub_nr</NAME>
<VALUE>99900</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>proj_desc</NAME>
<VALUE>Nagekomen kosten Oktober 2018</VALUE>
</FIELD>
<FIELD TYPE="N">
<NAME>pro_stat</NAME>
<VALUE>9</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>comment</NAME>
<VALUE></VALUE>
</FIELD>
</RECORD>
<RECORD>
<RECID>118811</RECID>
<FIELD TYPE="C">
<NAME>proj_code</NAME>
<VALUE>118811</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>sub_nr</NAME>
<VALUE>99901</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>proj_desc</NAME>
<VALUE>Nagekomen kosten November 2019</VALUE>
</FIELD>
<FIELD TYPE="N">
<NAME>pro_stat</NAME>
<VALUE>19</VALUE>
</FIELD>
<FIELD TYPE="C">
<NAME>comment</NAME>
<VALUE>wow</VALUE>
</FIELD>
</RECORD>
</EBUSQRYRS>
</EBUSMSGSRS>
</AVXML>
';
DECLARE #tbl TABLE (ID INT IDENTITY(1,1) PRIMARY KEY, RECID VARCHAR(10), [col_name] VARCHAR(20), [col_value] VARCHAR(100));
INSERT INTO #tbl
SELECT c.value('(../RECID/text())[1]', 'VARCHAR(100)') AS [RECID]
, c.value('(NAME/text())[1]', 'VARCHAR(30)') AS [name]
, c.value('(VALUE/text())[1]', 'VARCHAR(500)') AS [value]
FROM #xml.nodes('//RECORD/FIELD') AS t(c);
DECLARE #RowCount INT = (SELECT MAX(ID) FROM #tbl)
, #recID varchar(10) = (SELECT TOP(1) RECID FROM #tbl WHERE ID = 1)
, #xml_data VARCHAR(MAX) = '<root><RECORD>';
WHILE #RowCount > 0 BEGIN
SELECT #xml_data +=
IIF(#recID != RECID,
'<RECID>' + #recID + '</RECID>' +
'</RECORD><RECORD>',
'') +
CONCAT('<',[col_Name],'>',[col_value],'</',[col_Name],'>')
, #recID = RECID
FROM #tbl
ORDER BY ID DESC OFFSET #RowCount - 1 ROWS FETCH NEXT 1 ROWS ONLY;
SET #RowCount -= 1;
END;
SET #xml_data += '<RECID>' + #recID + '</RECID>' + '</RECORD></root>';
DECLARE #handler int;
exec sys.sp_xml_preparedocument #handler OUTPUT, #xml_data;
--print #xml_data
select *
from OPENXML(#handler,'/root/RECORD',11)
WITH
(
id nvarchar(50) 'RECID',
proj_code nvarchar(50) 'proj_code',
sub_nr nvarchar(50) 'sub_nr',
proj_desc nvarchar(1000) 'proj_desc',
pro_stat nvarchar(50) 'pro_stat',
comment nvarchar(50) 'comment'
)
exec sys.sp_xml_removedocument #handler

Append unit to a XML column value in SQL Server

I have a table with a XML column for storing the file details. Now I want to update the size node in the XML column with KB or MB according to the value.
Sample data:
<FileInfo>
<Field Name="Filename">PV_1_PV_4126_C-482N-25457-005_V1.pdf</Field>
<Field Name="Created">02/21/2017</Field>
<Field Name="Modified">02/21/2017</Field>
<Field Name="Uploaded By">2120</Field>
<Field Name="Uploaded On">02/21/2017</Field>
<Field Name="Size">755</Field>
</FileInfo>
Expected result:
<FileInfo>
<Field Name="Filename">PV_1_PV_4126_C-482N-25457-005_V1.pdf</Field>
<Field Name="Created">02/21/2017</Field>
<Field Name="Modified">02/21/2017</Field>
<Field Name="Uploaded By">2120</Field>
<Field Name="Uploaded On">02/21/2017</Field>
<Field Name="Size">755 KB</Field>
</FileInfo>
Sample data:
<FileInfo>
<Field Name="Filename">PV_1_PV_4126_C-482N-25457-005_V1.pdf</Field>
<Field Name="Created">02/21/2017</Field>
<Field Name="Modified">02/21/2017</Field>
<Field Name="Uploaded By">2120</Field>
<Field Name="Uploaded On">02/21/2017</Field>
<Field Name="Size">1024</Field>
</FileInfo>
Expected result:
<FileInfo>
<Field Name="Filename">PV_1_PV_4126_C-482N-25457-005_V1.pdf</Field>
<Field Name="Created">02/21/2017</Field>
<Field Name="Modified">02/21/2017</Field>
<Field Name="Uploaded By">2120</Field>
<Field Name="Uploaded On">02/21/2017</Field>
<Field Name="Size">1 MB</Field>
</FileInfo>
You can use an updateable CTE
DECLARE #dummy TABLE(YourXMLColumn XML);
INSERT INTO #dummy VALUES
(
N'<FileInfo>
<!--More fields-->
<Field Name="Size">755</Field>
</FileInfo> '
)
,(
N'<FileInfo>
<!--More fields-->
<Field Name="Size">1024</Field>
</FileInfo> '
);
WITH ReplaceValue AS
(
SELECT YourXMLColumn
,ca2.newSize
FROM #dummy AS d
CROSS APPLY(SELECT d.YourXMLColumn.value(N'(/FileInfo/Field[#Name="Size"]/text())[1]',N'int')) AS ca1(size)
CROSS APPLY(SELECT CASE WHEN ca1.size % 1024=0
THEN CAST(ca1.size/1024 AS VARCHAR(10)) + ' MB'
ELSE CAST(ca1.size AS VARCHAR(10)) + ' KB' END) AS ca2(newSize)
)
UPDATE ReplaceValue SET YourXMLColumn.modify(N'replace value of (/FileInfo/Field[#Name="Size"]/text())[1] with sql:column("newSize")');
SELECT * FROM #dummy;
First I read the value of the "Size" field. If it is divideable by 1024 it will be written as "MB", otherwise the value remains as "KB".
The final UPDATE will update the CTE, but this affects the tables column actually. The SELECT shows the modified table data.

How to get BCP to generate a Format File for importing fixed-width data into a SQL Server table?

The bcp command that I am using:
bcp TableName format nul -c -f c:\folder\TargetFile.xml -x -S ServerName -T -q
I think I just need the fields to have a type of xsi:type="CharFixed" rather then xsi:type="CharTerm".
The xml that it creates which doesn't work for me:
<?xml version="1.0"?>
<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RECORD>
<FIELD ID="1" xsi:type="CharTerm" TERMINATOR="\t" MAX_LENGTH="24" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="2" xsi:type="CharTerm" TERMINATOR="\t" MAX_LENGTH="150" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="3" xsi:type="CharTerm" TERMINATOR="\t" MAX_LENGTH="150" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="4" xsi:type="CharTerm" TERMINATOR="\t" MAX_LENGTH="20" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
<FIELD ID="5" xsi:type="CharTerm" TERMINATOR="\r\n" MAX_LENGTH="12" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
</RECORD>
<ROW>
<COLUMN SOURCE="1" NAME="UID" xsi:type="SQLNCHAR"/>
<COLUMN SOURCE="2" NAME="FNAME" xsi:type="SQLNCHAR"/>
<COLUMN SOURCE="3" NAME="LNAME" xsi:type="SQLNCHAR"/>
<COLUMN SOURCE="4" NAME="PHONE" xsi:type="SQLNCHAR"/>
<COLUMN SOURCE="5" NAME="Target" xsi:type="SQLNCHAR"/>
</ROW>
</BCPFORMAT>
What I actually need: (xsi:type="CharFixed")
<?xml version="1.0"?>
<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RECORD>
<FIELD ID="1" xsi:type="CharFixed" LENGTH="3"/>
<FIELD ID="2" xsi:type="CharFixed" LENGTH="3"/>
</RECORD>
<ROW>
<COLUMN SOURCE="1" NAME="Field1" xsi:type="SQLCHAR" LENGTH="3"/>
<COLUMN SOURCE="2" NAME="Field2" xsi:type="SQLCHAR" LENGTH="3"/>
</ROW>
</BCPFORMAT>
Here is the method I created to help me resolve my issue...
private XmlDocument CreateFormatFile()
{
const string xsiURI = "http://www.w3.org/2001/XMLSchema-instance";
var ff = new XmlDocument();
var dec = ff.CreateXmlDeclaration("1.0", null, null);
ff.AppendChild(dec);
var bcpFormat = ff.CreateElement("BCPFORMAT");
bcpFormat.SetAttribute("xmlns", "http://schemas.microsoft.com/sqlserver/2004/bulkload/format");
bcpFormat.SetAttribute("xmlns:xsi", xsiURI);
var record = ff.CreateElement("RECORD");
var row = ff.CreateElement("ROW");
for (var x = 0; x < Columns.Count; x++)
{
var col = Columns[x];
var id = (col.Index + 1).ToString();
var length = col.Length.ToString();
var column = ff.CreateElement("COLUMN");
column.SetAttribute("SOURCE", id);
column.SetAttribute("NAME", col.Name);
column.SetAttribute("type", xsiURI, "SQLCHAR");
column.SetAttribute("LENGTH", length);
var field = ff.CreateElement("FIELD");
field.SetAttribute("ID", id);
if (x != Columns.Count - 1)
{
field.SetAttribute("type", xsiURI, "CharFixed");
field.SetAttribute("LENGTH", length);
}
else
{
field.SetAttribute("type", xsiURI, "CharTerm");
field.SetAttribute("TERMINATOR", #"\r\n");
}
record.AppendChild(field);
row.AppendChild(column);
}
bcpFormat.AppendChild(record);
bcpFormat.AppendChild(row);
ff.AppendChild(bcpFormat);
return ff;
}
Try using the bcp Native Format Option:
How bcp Handles Data in Native Format
...
char or varchar data
At the beginning of each char or varchar field, bcp adds the prefix length.
You'd use the "-n" option instead of "-c":
bcp TableName format nul -n -f c:\folder\TargetFile.xml -x -S ServerName -T -q

How to parse data from a CLOB in SQL Server?

I would like to use OPENROWSET with the BULK command to load data into SQL Server as a CLOB, and as a second step to parse the CLOB and load read the data as a table.
E.g.:
SELECT BulkColumn
FROM OPENROWSET (BULK 'c:\somedir\somefile.txt', SINGLE_CLOB) TheFile
yields:
BulkColumn
Col1,Col2,Col3,1,2,3,1,2,3,1,2,3,1,2,3
I want to select this as:
Col1 Col2 Col3
1 2 3
1 2 3
1 2 3
Create a format file and use OPENROWSET or BULK INSERT to import data from text file.
Example format.xml:
<?xml version="1.0" ?>
<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RECORD>
<FIELD ID="1" xsi:type="CharTerm" TERMINATOR="," MAX_LENGTH="1" />
<FIELD ID="2" xsi:type="CharTerm" TERMINATOR="," MAX_LENGTH="1" />
<FIELD ID="3" xsi:type="CharTerm" TERMINATOR="," MAX_LENGTH="1" />
</RECORD>
<ROW>
<COLUMN SOURCE="1" NAME="Col1" xsi:type="SQLNVARCHAR" />
<COLUMN SOURCE="2" NAME="Col2" xsi:type="SQLNVARCHAR" />
<COLUMN SOURCE="3" NAME="Col3" xsi:type="SQLNVARCHAR" />
</ROW>
</BCPFORMAT>
This is the key line:
<FIELD ID="3" xsi:type="CharTerm" TERMINATOR="," MAX_LENGTH="1" />
where the terminator char is "," and not "\r\n", if the data is in a single line.
Example OPENROWSET:
INSERT INTO [your_table]
SELECT [text_file].[Col1],
[text_file].[Col2],
[text_file].[Col3]
FROM OPENROWSET(
BULK N'c:\somedir\somefile.txt',
FORMATFILE = N'c:\somedir\format.xml',
FIRSTROW = 1) AS [text_file]
Example BULK INSERT:
BULK INSERT [your_table]
FROM N'c:\somedir\somefile.txt'
WITH (FORMATFILE = N'c:\somedir\format.xml')
Don't import it as OPENROWSET..BULK
You can use BULK INSERT to import it as columns or OPENROWSET by itself