PowerShell 中对 Invoke-Sqlcmd 的 Unicode 支持
Posted
技术标签:
【中文标题】PowerShell 中对 Invoke-Sqlcmd 的 Unicode 支持【英文标题】:Unicode support for Invoke-Sqlcmd in PowerShell 【发布时间】:2012-08-07 23:46:58 【问题描述】:PowerShell sqlps module 为从 PowerShell 中访问 SQL Server 提供核心支持,其 Invoke-Sqlcmd cmdlet 是其执行文字查询或 SQL 脚本文件的主要主力(类似于非 PowerShell sqlcmd 实用程序)。我最近尝试了一些实验来确认 Invoke-Sqlcmd 处理 Unicode 并且有一些令人惊讶的结果。
我从这个简单的脚本文件(名为 unicode.sql)开始:
CREATE TABLE #customers
( [IdCust] int,
[FirstName] nvarchar(25),
[SurName] nvarchar(25)
);
INSERT INTO #customers VALUES (4, N'Hans', N'Grüßner')
SELECT * FROM #customers;
DROP TABLE #customers;
请注意,姓氏包含一些典型的 Unicode 字符,例如,您可能会在德语名称中找到这些字符。
结果
SQL Server Management Studio:在输出到网格或文本时正确呈现,例如
IdCust FirstName Surname
----------- ------------------------- -------------------------
4 Hans Grüßner
sqlcmd 实用程序:无论是从 DOS shell 还是 PowerShell 运行,都可以正确呈现,例如
C:\> sqlcmd -S .\SQLEXPRESS -i unicode.sql
IdCust FirstName Surname
----------- ------------------------- -------------------------
4 Hans Grüßner
PowerShell Invoke-Sqlcmd:呈现不正确(无论是输出为如下所示的文本还是通过管道传输到 Out-Gridview):
PS> Invoke-Sqlcmd -Server .\sqlexpress -InputFile unicode.sql
IdCust FirstName Surname
------ --------- -------
4 Hans Gr??ner
Invoke-Sqlcmd 的MSDN documentation 只是顺便提到了 Unicode,将其命令行开关与 sqlcmd 的开关进行比较,表明后者有一个用于输出 Unicode 的-u
选项(在我的上面的实验),Invoke-Sqlcmd 有 no 等效参数。
通过广泛的网络搜索,我没有发现任何关于这一点的信息,但我仍然希望这在某种程度上是我的用户错误。在 PowerShell 中使用 Invoke-Sqlcmd 检索输入数据时,有没有办法保留输入数据?
【问题讨论】:
我认为您应该更改您正在使用的 SQL Server 的字符编码。 你的 $OutputEncoding 的 Powershell 设置是什么?有趣的问题,我期待有时间来测试一下。 $OutputEncoding 设置为 US-ASCII。我只是尝试将其设置为[Text.Encoding]::Unicode
,然后设置为[Text.Encoding]::utf8
,但在这两种情况下都没有改变结果。
【参考方案1】:
更新我在另一台机器上测试了 invoke-sqlcmd 并且它可以工作,所以可能其余的不适用...
更新 2 似乎只有在通过 -Query 参数 invoke-sqlcmd 执行时出现 -inputfile 问题。
据我所知,这与转换字符串时的 ADO.NET DataTable 有关。当您使用 ExecuteScaler 或 ExecuteReader 时,它可以正常工作。当然,这并不能修复 invoke-sqlcmd,但可以解释原因:
$server = "$env:computername\sql1"
$database = "tempdb"
$query = @"
CREATE TABLE #customers
( [SurName] nvarchar(25)
);
INSERT INTO #customers VALUES (N'Grüßner')
SELECT * FROM #customers;
"@
$connection=new-object System.Data.SqlClient.SQLConnection
$connection.ConnectionString="Server=0;Database=1;Integrated Security=True" -f $server,$database
$command=new-object system.Data.SqlClient.SqlCommand($query,$connection)
$connection.Open()
$command.ExecuteScalar()
$connection.Close()
更新 3 文件的编码似乎是关键。查看 [System.IO.File]::ReadAllText,MSDN 文档指出它只会检测 UTF-8 或 UTF-32 编码。 http://msdn.microsoft.com/en-us/library/ms143369(v=vs.90).aspx
如果我使用 UTF-8 保存 .sql 文件,则使用 -inputfile 参数有效。在 SSMS 中保存 .sql 文件时可以选择 UTF-8,但这里有一些 Powershell 代码也可以检查和更改编码。你需要从http://poshcode.org/2075获取Get-FileEncoding.ps1
. .\Get-FileEncoding.ps1
Get-FileEncoding -Path E:\bin\unicode.sql
$query = get-content E:\bin\unicode.sql
$query= $query -join "`n"
$query | Out-File -FilePath e:\bin\unicode.sql -Encoding UTF8 -force
Get-FileEncoding -Path E:\bin\unicode.sql
【讨论】:
感谢您的信息,Chad,但有几个问题。当我尝试运行您的代码时,我得到“关键字'VALUES'附近的语法不正确”——即使查询本身在SSMS中运行良好。有什么办法可以解决这个错误吗?...所以我尝试将查询设置为select N'Grüßner' as surname
但这显然不是一个有效的等价物,因为它甚至可以与 Invoke-Sqlcmd 一起使用。
使用 here-strings aka 逐字字符串,您需要单独运行它们,通过 "@ 单独复制并通过 $query。此外,使用 Powershell_ise 更容易,您不必担心。因为你说的可能不是一个有效的测试。
见更新2。当使用 -Query 参数时,您的代码可以与 -inputfile 一起正常工作。
由于您将问题隔离到-InputFile
,因此我将 input 与 output 分开。首先,我创建了一个 non-temp 表并插入了 unicode 名称。然后select * from uni_customers
正确检索它是否使用-Query
或-InputFile
,所以输出工作正常。我清空了输入测试的表,然后做了两个单独的插入。 select *
现在返回两行,一行好(用 -Query 插入),一行坏(用 -InputFile 插入)。 结论: 使用 PowerShell 从数据库中检索到的数据是有效的;仅在插入时且仅从有问题的文件中插入。
好收获。根据您的观察,我进一步缩小了范围。如果 .sql 文件使用 UTF-8 或 UTF-32 编码,则输入文件将起作用。我已经发布了一些代码和解释。【参考方案2】:
执行SQLCMD时,必须指定编码。
EXEC xp_cmdshell 'for %f in ("Dir*.sql") do sqlcmd -S Server -U username -P password -d database -i "%f" -b -f 65001'
【讨论】:
以上是关于PowerShell 中对 Invoke-Sqlcmd 的 Unicode 支持的主要内容,如果未能解决你的问题,请参考以下文章
如何在 PowerShell 的 Get-ChildItem -Exclude cmdlet 中对目录使用通配符