帮助我使用 powershell 和 bcp 将 CSV 加载到 SQL Server
我正在使用 bcp 从远方其他人从另一个表导出的 CSV 加载表,但遇到了一些问题。我原来的两个问题:一个导出的字段是一个 int,需要以 varchar 字段结束,另一个字段需要用静态字符串填充。嗯,第一个问题没什么大不了的,Chad 的回答让我想到了 @{n='Col3';e={'ABC'}}
语法。但我在正确加载数据方面遇到了几个问题。
有时一个值可能没有空格,有时可能有。如何在 bcp 格式文件中指定分隔符?我问这个问题是因为我可能需要用引号将它们引起来。
a.我应该用引号将中间 CSV 文件中的值括起来吗?
在我的测试代码中,它将列标题和换行符推送到 Col1 中,并使用第一行的 Col1 的实际值。
在我实际的、未经消毒的代码中,我在第一行得到
字符串数据,右截断
,尽管第二行插入正常。
无论如何,我从其他地方获得的初始 CSV 数据看起来像这样(注意:没有标题行)
"ABC123",123456,"APPLE"
"XYZ789",456789,"ORANGE"
目标 SQL 表是这样的
Col1 varchar(50) (Primary Key)
Col2 varchar(50)
Col3 varchar(50)
Col4 varchar(50)
我需要将 ABC123 加载到 Col1
中, 123456 导入 Col2
,Export1 导入 Col3
,APPLE 导入 Col4
。 Export1 是静态字符串。我在 Powershell 1.0 中执行此操作。
编辑:乍得的导入-csv | export-csv 看起来很有前途,但它不喜欢缺少标头,而且 PS 1.0 不支持 -Header 选项。
编辑:编辑上面的描述以反映我是如何走到这一点的。四列的表格和三列的 CSV 显然是简化的。一个真实的列是一座城市,因此它可以包含简单的字符串或需要引用的字符串。此时我的Powershell 1.0代码如下。
$SQLSERVER="svr"
$SQLTABLE="test"
$SQLUSER="u"
$SQLPASS="p"
$TESTFILE = "testdata.csv"
$TESTFILEHDR = "testdata-wHeaders.csv"
$TESTFILEFIX = "testdata-fixed.csv"
$OrigHeaders = "`"Col1`",`"Col2`",`"Col3`"`n"
function Create-BcpFormat($fileName)
{
@"
<?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=',"' COLLATION='SQL_AltDiction_CP850_CI_AS'/>
<FIELD ID='2' xsi:type='CharTerm' TERMINATOR='",' COLLATION='SQL_AltDiction_CP850_CI_AS'/>
<FIELD ID='3' xsi:type='CharTerm' TERMINATOR=',' COLLATION='SQL_AltDiction_CP850_CI_AS'/>
<FIELD ID='4' xsi:type='CharTerm' TERMINATOR='\r\n' COLLATION='SQL_AltDiction_CP850_CI_AS'/>
</RECORD>
<ROW>
<COLUMN SOURCE="1" NAME="Col1" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="2" NAME="Col2" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="3" NAME="Col3" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="4" NAME="Col4" xsi:type="SQLVARYCHAR"/>
</ROW>
</BCPFORMAT>
"@ | Set-Content $filename
}
## GHI456 is already in the table, clean out previous attempts
"`nclean test table:"
osql @("-S","$SQLSERVER","-U","$SQLUSER","-P","$SQLPASS","-Q",
"""delete from $SQLTABLE where col1 <> 'GHI456' or col1 is null""")
## Prepend
$body = [string]::join([environment]::NewLine, (gc $TESTFILE))
$OrigHeaders + $body > $TESTFILEHDR
"`nTESTFILEHDR:"
type $TESTFILEHDR
$accts = Import-csv $TESTFILEHDR | select 'Col1', 'Col2', @{n='Col3';e={'ABC'}}, @{n='Col4';e={$_.Col3}}
$accts
$accts | Export-Csv $TESTFILEFIX -NoTypeInfo
"`nTESTFILEFIX:"
type $TESTFILEFIX
$BCPFMTFILE = "bcp.fmt"
$BCPERRFILE = "bcp.err"
Create-BcpFormat $BCPFMTFILE
bcp @("$SQLTABLE","in","$TESTFILEFIX","-S","$SQLSERVER","-U","$SQLUSER","-P","$SQLPASS","-f",$BCPFMTFILE,"-e",$BCPERRFILE)
Remove-Item $BCPFMTFILE
"`ntest table:"
osql @("-S","$SQLSERVER","-U","$SQLUSER","-P","$SQLPASS","-Q",
"""select left(Col1,20) 'Col1', left(Col2,8) 'Col2', left(Col3,8) 'Col3', left(Col4,8) 'Col4' from $SQLTABLE""")
"`nBCPERRFILE:"
type $BCPERRFILE
I'm using bcp to load a table from a CSV exported from another table by someone else far far away, and have run into some issues. My original two problems: one exported field is an int that needs to end up in a varchar field, and another field needs to be populated with a static string. Well, the first is no big deal, and Chad's answer led me to the @{n='Col3';e={'ABC'}}
syntax. But i'm stumbling around several issues getting the data loaded correctly.
Sometimes a value might have no spaces, sometimes it might. How do i specify delimiters for that in a bcp format file? I ask because presumably i need to surround these with quotes.
a. Should i be wrapping values in the intermediate CSV file with quotes?
In my test code, it's shoving the column headers and a linefeed into Col1 with the actual value of Col1 for the first row.
In my actual, unsanitized code, i'm getting
String data, right truncation
on the first row, though the second row is inserted okay.
Anyhow, the initial CSV data that i get from elsewhere looks like this (note: no header row)
"ABC123",123456,"APPLE"
"XYZ789",456789,"ORANGE"
The target SQL table is like this
Col1 varchar(50) (Primary Key)
Col2 varchar(50)
Col3 varchar(50)
Col4 varchar(50)
I need to load ABC123 into Col1
, 123456 into Col2
, Export1 into Col3
, and APPLE into Col4
. Export1 is the static string. I'm doing this in Powershell 1.0.
EDIT: Chad's import-csv | export-csv looks promising, but it doesn't like missing headers, and PS 1.0 doesn't support the -Header option.
EDIT: Edited description above to reflect how i've wandered to this point. The four-column table and three-column CSV are obviously simplifications. One real column is a city, so it could contain simple strings or those that require quoting. My Powershell 1.0 code at this point is as follows.
$SQLSERVER="svr"
$SQLTABLE="test"
$SQLUSER="u"
$SQLPASS="p"
$TESTFILE = "testdata.csv"
$TESTFILEHDR = "testdata-wHeaders.csv"
$TESTFILEFIX = "testdata-fixed.csv"
$OrigHeaders = "`"Col1`",`"Col2`",`"Col3`"`n"
function Create-BcpFormat($fileName)
{
@"
<?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=',"' COLLATION='SQL_AltDiction_CP850_CI_AS'/>
<FIELD ID='2' xsi:type='CharTerm' TERMINATOR='",' COLLATION='SQL_AltDiction_CP850_CI_AS'/>
<FIELD ID='3' xsi:type='CharTerm' TERMINATOR=',' COLLATION='SQL_AltDiction_CP850_CI_AS'/>
<FIELD ID='4' xsi:type='CharTerm' TERMINATOR='\r\n' COLLATION='SQL_AltDiction_CP850_CI_AS'/>
</RECORD>
<ROW>
<COLUMN SOURCE="1" NAME="Col1" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="2" NAME="Col2" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="3" NAME="Col3" xsi:type="SQLVARYCHAR"/>
<COLUMN SOURCE="4" NAME="Col4" xsi:type="SQLVARYCHAR"/>
</ROW>
</BCPFORMAT>
"@ | Set-Content $filename
}
## GHI456 is already in the table, clean out previous attempts
"`nclean test table:"
osql @("-S","$SQLSERVER","-U","$SQLUSER","-P","$SQLPASS","-Q",
"""delete from $SQLTABLE where col1 <> 'GHI456' or col1 is null""")
## Prepend
$body = [string]::join([environment]::NewLine, (gc $TESTFILE))
$OrigHeaders + $body > $TESTFILEHDR
"`nTESTFILEHDR:"
type $TESTFILEHDR
$accts = Import-csv $TESTFILEHDR | select 'Col1', 'Col2', @{n='Col3';e={'ABC'}}, @{n='Col4';e={$_.Col3}}
$accts
$accts | Export-Csv $TESTFILEFIX -NoTypeInfo
"`nTESTFILEFIX:"
type $TESTFILEFIX
$BCPFMTFILE = "bcp.fmt"
$BCPERRFILE = "bcp.err"
Create-BcpFormat $BCPFMTFILE
bcp @("$SQLTABLE","in","$TESTFILEFIX","-S","$SQLSERVER","-U","$SQLUSER","-P","$SQLPASS","-f",$BCPFMTFILE,"-e",$BCPERRFILE)
Remove-Item $BCPFMTFILE
"`ntest table:"
osql @("-S","$SQLSERVER","-U","$SQLUSER","-P","$SQLPASS","-Q",
"""select left(Col1,20) 'Col1', left(Col2,8) 'Col2', left(Col3,8) 'Col3', left(Col4,8) 'Col4' from $SQLTABLE""")
"`nBCPERRFILE:"
type $BCPERRFILE
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
为什么不在 PowerShell 中以编程方式使用 ADO.NET 的 SqlBulkCopy?
您将拥有完全的控制权:逐行读取、转换数据并根据需要填充 DataTable 并批量调用 WriteToServer。
请参阅 http://msdn.microsoft.com/en -us/library/system.data.sqlclient.sqlbulkcopy.aspx
Why don't you use ADO.NET's SqlBulkCopy programmatically in PowerShell?
You will have full control: read line by line, convert data and populate a DataTable as you wish and invoke WriteToServer in batches.
See http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx
您可以执行类似的操作来添加列
import-csv .\forum.csv -Header "Col1","Col2","Col4" |选择 Col1、Col2、@{n='Col3';e={'Export1'}}、Col4 |导出-CSV ./forum2.csv -NoTypeInfo
You could do something like this to add the column
import-csv .\forum.csv -Header "Col1","Col2","Col4" | Select Col1, Col2, @{n='Col3';e={'Export1'}}, Col4 | Export-Csv ./forum2.csv -NoTypeInfo