PowerShell has become the ultimate choice for many database administrators because of its efficient way of handling and managing automation in a simple, quick way. It’s built on .NET Framework and uses Object Models such as COM, ADSI, ADO, and WMI. PowerShell has replaced the traditional way of scripting that used many legacy scripting practices to monitor SQL instances.
PowerShell已成为许多数据库管理员的最终选择,因为它以简单,快速的方式有效地处理和管理自动化。 它基于.NET Framework构建,并使用对象模型,例如COM,ADSI,ADO和WMI。 PowerShell已取代了传统的脚本编写方式,该方法使用了许多旧式脚本编写实践来监视SQL实例。
I’ve been asked on several occasions about how to store the output of PowerShell WMI data into the SQL table. The question comes up so frequently that I decided to write this article.
我曾多次被问到如何将PowerShell WMI数据的输出存储到SQL表中。 这个问题经常出现,我决定写这篇文章。
When sending data within a system (such as a PowerShell object to a cmdlet), the process is straightforward. However, with non-native data interchange (for instance, WMI to SQL), the process can potentially get complicated. Due to this, many purists suggest sticking to simple interchange formats, such as CSV, JSON or in some cases, XML.
在系统内发送数据(例如将PowerShell对象发送到cmdlet)时,该过程非常简单。 但是,使用非本地数据交换(例如,从WMI到SQL),该过程可能会变得很复杂。 因此,许多纯粹主义者建议坚持使用简单的交换格式,例如CSV,JSON或在某些情况下使用XML。
Let’s get out and see the possible options to transform WMI data to SQL table. In this article, we will:
让我们出发,看看将WMI数据转换为SQL表的可能选项。 在本文中,我们将:
This guide details the working example of checking disk space by querying WMI.
本指南详细介绍了通过查询WMI检查磁盘空间的工作示例。
Microsoft brought in WMI in order to simplify the management of the different classes of operating systems. While automation was one of the reasons for bringing WMI, the primary focus was to provide developers with handles that they could use when creating applications for Windows. But of course, WMI has these nifty uses as well.
Microsoft引入了WMI,以简化对不同类别的操作系统的管理。 自动化是带来WMI的原因之一,但主要重点是为开发人员提供在创建Windows应用程序时可以使用的句柄。 但是当然,WMI也具有这些漂亮的用途。
The simplest way to get the disk space information is with the Win32_LogicalDisk class. We filter the content to pick only DriveType=3 which is the type number for local drives.
获取磁盘空间信息的最简单方法是使用Win32_LogicalDisk类。 我们过滤内容以仅选择DriveType = 3,这是本地驱动器的类型号。
Get-WmiObject win32_logicaldisk -ComputerName -Filter "Drivetype=3" |`
select SystemName,DeviceID,VolumeName,@{Label="Total Size";Expression={$_.Size / 1gb -as [int] }},@{Label="Free Size";Expression={$_.freespace / 1gb -as [int] }}|Format-Table -AutoSize
We discuss the transformation of the above data into a SQL Table using some direct as well as indirect methods in this post:
在本文中,我们将讨论使用一些直接方法和间接方法将上述数据转换为SQL表:
Before we proceed, let’s look at what we need before we can proceed:
在继续之前,让我们先看一下我们需要什么:
Let’s now start transforming the data we received, into a SQL table.
现在开始将接收到的数据转换为SQL表。
The Invoke-Sqlcmd is a wrapper class and PowerShell version of SQL Server sqlcmd command with additional capabilities such as data manipulation and data transformations with a focus on the output data.
Invoke-Sqlcmd是SQL Server sqlcmd命令的包装器类和PowerShell版本,具有其他功能,例如数据处理和数据转换(着重于输出数据)。
The process is pretty simple:
这个过程非常简单:
CREATE TABLE tbl_PoShDisk
(
[SystemName] VARCHAR(40) not null,
[DeviceID] VARCHAR(40) not null,
[VolumeName] VARCHAR(40) not null,
[TotalSize] int not null,
[FreeSize] int not null
)
Let us prepare the script
让我们准备脚本
#Declare Servername
$sqlServer='hqdbsp18'
#Invoke-sqlcmd Connection string parameters
$params = @{'server'='HQDBT01';'Database'='SQLShackDemo'}
#Fucntion to manipulate the data
Function writeDiskInfo
{
param($server,$devId,$volName,$frSpace,$totSpace)
$totSpace=[math]::Round(($totSpace/1073741824),2)
$frSpace=[Math]::Round(($frSpace/1073741824),2)
$usedSpace = $totSpace - $frspace
$usedSpace=[Math]::Round($usedSpace,2)
# Data preparation for loading data into SQL table
$InsertResults = @"
INSERT INTO [SQLShackDemo].[dbo].[tbl_PosHdisk](SystemName,DeviceID,VolumeName,TotalSize,FreeSize)
VALUES ('$SERVER','$devId','$volName',$totSpace,$usedSpace)
"@
#call the invoke-sqlcmdlet to execute the query
Invoke-sqlcmd @params -Query $InsertResults
}
#Query WMI query to store the result in a varaible
$dp = Get-WmiObject win32_logicaldisk -ComputerName $sqlServer| Where-Object {$_.drivetype -eq 3}
#Loop through array
foreach ($item in $dp)
{
#Call the function to transform the data and prepare the data for insertion
writeDiskInfo $sqlServer $item.DeviceID $item.VolumeName $item.FreeSpace $item.Size
}
#Query the destination table to view the result
Invoke-Sqlcmd @params -Query "SELECT * FROM tbl_PosHdisk" | format-table -AutoSize
Invoke-Sqlcmd @params -Query "SELECT * FROM tbl_PosHdisk" | format-table -AutoSize
The below screen shot shows the steps and the results. The output is queried from the table tbl_PoShDisk
下面的屏幕截图显示了步骤和结果。 从表tbl_PoShDisk查询输出
There we go; we see how the data from the WMI query was transferred to an SQL Table
我们去了; 我们将看到WMI查询中的数据如何传输到SQL表
Another method to execute a query in PowerShell is to use ADO.NET libraries, which requires creating a DataSet, then creating a DataAdapter, and finally filling the DataAdapter. For many data retrieval needs from scripts ADO.NET can be a little too heavy. Fortunately, there are several ways to make this task simpler and still retain the benefits of the .NET DataTables. Let’s look at three methods to get SQL Server data from PowerShell. Once you have the data in DataTable, we can transform the data into a number of things including piping the output to one of the built-in cmdlets.
在PowerShell中执行查询的另一种方法是使用ADO.NET库,这需要创建一个DataSet,然后创建一个DataAdapter,最后填充DataAdapter。 对于许多来自脚本的数据检索需求,ADO.NET可能会有点过于繁重。 幸运的是,有多种方法可以简化此任务,并且仍然保留.NET DataTables的优点。 让我们看一下从PowerShell中获取SQL Server数据的三种方法。 将数据存储在DataTable中之后,我们可以将数据转换为许多内容,包括将输出传递给内置cmdlet之一。
ADO.NET is a set of class libraries that are part of the .NET Framework. The ADO.NET classes are generally divided into two types: connected classes and disconnected classes.
ADO.NET是.NET Framework一部分的一组类库。 ADO.NET类通常分为两种类型:连接类和断开连接的类。
Connected class
连接类
Disconnected classes
断开类
Script preparation
脚本准备
#Invoke-sqlcmd Connection string parameters
$params = @{'server'='HQDBT01';'Database'='SQLShackDemo'}
#Server to query WMI class win32_logicalDisks
$server = 'hqdbsp18'
#Prepare Insert Statement
$insert = @'
INSERT INTO [SQLShackDemo].[dbo].[tbl_PosHdisk](SystemName,DeviceID,VolumeName,TotalSize,FreeSize)
VALUES ('{0}','{1}','{2}','{3}','{4}')
'@
Try {
#Define connction string of target database
$connectionString = 'Data Source=HQDBT01;Initial Catalog=SQLShackDemo;Integrated Security=SSPI'
# connection object initialization
$conn = New-Object System.Data.SqlClient.SqlConnection($connectionString)
#Open the Connection
$conn.Open()
# Prepare the SQL
$cmd = $conn.CreateCommand()
#WMI ouput transformation to SQL table
Get-WmiObject win32_logicaldisk -ComputerName $server -Filter "Drivetype=3" |`
select SystemName,DeviceID,VolumeName,@{Label="TotalSize";Expression={$_.Size / 1gb -as [int] }},@{Label="FreeSize";Expression={$_.freespace / 1gb -as [int] }}|`
ForEach-Object{
$cmd.CommandText = $insert -f $_.SystemName, $_.DeviceID, $_.VolumeName, $_.TotalSize, $_.FreeSize
$cmd.ExecuteNonQuery()
}
#Close the connection
$conn.Close()
}
Catch {
Throw $_
}
Invoke-Sqlcmd @params -Query "SELECT * FROM tbl_PosHdisk" | format-table -AutoSize
The output is given below
输出如下
We write a function to perform the copy operation. The function, Out-DataTable can be found in Appendix (A). This function takes care of converting the output of the WMI query to the data table. The data-table output is then fed to the SqlBulkCopy class in order to write the data to the SQL table. The SqlBulkCopy class loads a SQL Server table with data from another source which in this case is Win32_LogicalDisks.
我们编写一个函数来执行复制操作。 函数Out-DataTable可以在附录(A)中找到 。 此函数负责将WMI查询的输出转换为数据表。 然后,将数据表的输出馈送到SqlBulkCopy类,以便将数据写入SQL表。 SqlBulkCopy类使用来自另一个源(在本例中为Win32_LogicalDisks)的数据加载SQL Server表。
#Invoke-sqlcmd Connection string parameters
$params = @{'server'='HQDBT01';'Database'='SQLShackDemo'}
#function to retrieve disk information
Function Get-DisksSpace ([string]$Servername)
{
Get-WmiObject win32_logicaldisk -ComputerName $Servername -Filter "Drivetype=3" |`
select SystemName,DeviceID,VolumeName,@{Label="Total SIze";Expression={$_.Size / 1gb -as [int] }},@{Label="Free Size";Expression={$_.freespace / 1gb -as [int] }}
}
#Variable to hold output as data-table
$dataTable = Get-DisksSpace hqdbsp18 | Out-DataTable
#Define Connection string
$connectionString = "Data Source=hqdbt01; Integrated Security=True;Initial Catalog=SQLShackDemo;"
#Bulk copy object instantiation
$bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $connectionString
#Define the destination table
$bulkCopy.DestinationTableName = "tbl_PosHdisk"
#load the data into the target
$bulkCopy.WriteToServer($dataTable)
#Query the target table to see for output
Invoke-Sqlcmd @params -Query "SELECT * FROM tbl_PosHdisk" | format-table -AutoSize
We can also use SQL constructs with the PowerShell Export options. We can:
我们还可以将SQL构造与PowerShell导出选项一起使用。 我们可以:
Let’s look at these processes one by one.
让我们一一看一下这些过程。
The ConvertTo-Json cmdlet converts an object to a valid JSON string. Using this cmdlet, the output is converted to JSON (JavaScript Object Notation). The JSON file is then queried using JSON construct in SQL Server, called OPEN JSON, to transform the data form the win32_LogicalDisk WMI class, to a relational table.
ConvertTo-Json cmdlet将对象转换为有效的JSON字符串。 使用此cmdlet,输出将转换为JSON(JavaScript对象表示法)。 然后,使用SQL Server中称为OPEN JSON的 JSON构造查询JSON文件,以将数据从win32_LogicalDisk WMI类转换为关系表。
The WMI output is stored in a variable which is then written to a JSON file using Out-File formatting cmdlet.
WMI输出存储在一个变量中,然后使用Out-File格式cmdlet将其写入JSON文件。
$JSON=Get-WmiObject win32_logicaldisk -ComputerName hqdbsp18 -Filter "Drivetype=3" |select SystemName,DeviceID,VolumeName,@{Label="Total SIze";Expression={$_.Size / 1gb -as [int] }},@{Label="Free Size";Expression={$_.freespace / 1gb -as [int] }} |ConvertTo-Json
$json |Out-File \\hqdbt01\f$\PowerSQL\DiskSpace.JSON
The output of JSON file is shown below
JSON文件的输出如下所示
Let’s now feed the JSON into an SQL table
现在让我们将JSON输入到SQL表中
SELECT t.*
FROM
OPENROWSET(BULK N'\\hqdbt01\f$\PowerSQL\DiskSpace.JSON', SINGLE_NCLOB) AS JSON
CROSS APPLY OPENJSON(BulkColumn)
WITH(
Server NVARCHAR(10),
DeviceID NVARCHAR(20),
VolumeName NVARCHAR(20),
[Total SIze] DECIMAL(5,2),
[Free Size] DECIMAL(5,2)
) AS t
The data can be fed to table using the Insert SQL statement
可以使用Insert SQL语句将数据提供给表
The XML data comes from the XML file and is stored in a column bulkcolumn; use the XML methods, extract values with xml.value(), project nodes with xml.nodes(), use CROSS APPLY to join its nodes to derive all the values of the nodes.
XML数据来自XML文件,并存储在一列bulkcolumn中。 使用XML方法,使用xml.value()提取值,使用xml.nodes()提取项目节点,使用CROSS APPLY联接其节点以导出节点的所有值。
The below query writes WMI output to XML File
下面的查询将WMI输出写入XML文件
Get-WmiObject win32_logicaldisk -ComputerName hqdbsp18 -Filter "Drivetype=3" |`
select SystemName,DeviceID,VolumeName,@{Label="Total SIze";Expression={$_.Size / 1gb -as [int] }},@{Label="Free Size";Expression={$_.freespace / 1gb -as [int] }} |ConvertTo-Xml -as String -NoTypeInformation|`
Set-Content -path \\hqdbt01\f$\PowerSQL\DiskSpace.xml
The below is the generated XML file
下面是生成的XML文件
CREATE TABLE tbl_XMLDisk
(
XMLData XML,
)
--truncate table XMLwithOpenXML
INSERT INTO tbl_XMLDisk(XMLData)
SELECT
CONVERT(XML, BulkColumn) AS BulkColumn
FROM
OPENROWSET(BULK 'f:\PowerSQL\DiskSpace.xml', SINGLE_BLOB) AS x;
SELECT XMLData FROM tbl_XMLDisk
SELECT
p.value('(./Property)[1]', 'VARCHAR(20)') AS SystemName,
p.value('(./Property)[2]', 'VARCHAR(30)') AS DeviceID,
p.value('(./Property)[3]', 'VARCHAR(30)') AS VolumeName,
p.value('(./Property)[4]', 'int') AS [Total SIze],
p.value('(./Property)[5]', 'int') AS [Free Size]
FROM tbl_XMLDisk
CROSS APPLY XMLData.nodes('/Objects/Object') t(p)
The XML Query output
XML查询输出
The first statement we’ll look at is BULK INSERT, which lets you import data from a data file into a table In the following example, I import the data from the CSV file into the SQL table
我们将要看的第一条语句是BULK INSERT ,它使您可以将数据文件中的数据导入表中。在下面的示例中,我将数据文件从CSV文件中导入SQL表中。
Get-WmiObject win32_logicaldisk -ComputerName hqdbsp18 -Filter "Drivetype=3" |`
select SystemName,DeviceID,VolumeName,@{Label="Total SIze";Expression={$_.Size / 1gb -as [int] }},@{Label="Free Size";Expression={$_.freespace / 1gb -as [int] }} |`
ConvertTo-Csv -NoTypeInformation| Set-Content -path \\hqdbt01\f$\PowerSQL\DiskSpace.csv
--Create table.
CREATE TABLE tbl_CSVDisk
(
[SystemName] VARCHAR(40),
[DeviceID] VARCHAR(40),
[VolumeName] VARCHAR(40),
[TotalSize] VARCHAR(40),
[FreeSize] VARCHAR(40)
)
--Load the data into the SQL table starting with 2 row, comma(‘,’) as delimiter and newline as
--rowseparator
BULK
INSERT tbl_CSVDisk
FROM 'F:\PowerSQL\DiskSpace.csv'
WITH
(
FIRSTROW = 2,
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\n'
)
GO
--Check the content of the table.
SELECT REPLACE(systemName,'"','') systemName,
REPLACE([DeviceID],'"','') [DeviceID],
REPLACE([VolumeName],'"','') [VolumeName],
REPLACE([TotalSize],'"','') [TotalSize],
REPLACE([FreeSize],'"','') [FreeSize]
FROM tbl_CSVDisk
GO
Different methods and various techniques can be used to achieve a specific result. The challenge is to use the right tool, the right way, for the right job: the proverbial “Driving a screw with a hammer” problem. When presented with a new tool set, we shouldn’t try and use it the same way as other tools we’ve had in the past. Instead, we must learn the tool so we can make the best use of it for your process.
可以使用不同的方法和各种技术来获得特定的结果。 挑战在于使用正确的工具,正确的方法来完成正确的工作:众所周知的“用锤子驱动螺丝”问题。 当提供新工具集时,我们不应尝试使用与过去使用过的其他工具相同的方式。 相反,我们必须学习该工具,以便我们可以在您的过程中充分利用它。
In this case, we may have to consider the efficiency of the methods we listed, and make a decision about which method to use.
在这种情况下,我们可能必须考虑所列方法的效率,并决定要使用哪种方法。
Function Out-DataTable
{
$dt = new-object Data.datatable
$First = $true
foreach ($item in $input){
$DR = $DT.NewRow()
$Item.PsObject.get_properties() | foreach {
if ($first) {
$Col = new-object Data.DataColumn
$Col.ColumnName = $_.Name.ToString()
$DT.Columns.Add($Col) }
if ($_.value -eq $null) {
$DR.Item($_.Name) = "[empty]"
}
elseif ($_.IsArray) {
$DR.Item($_.Name) =[string]::Join($_.value ,";")
}
else {
$DR.Item($_.Name) = $_.value
}
}
$DT.Rows.Add($DR)
$First = $false
}
return @(,($dt))
}
翻译自: https://www.sqlshack.com/6-methods-write-powershell-output-sql-server-table/