[英]T-SQL select matching rows from multiple columns delimited by carriage returns (BGInfo output)
BGInfo(Microsoft Sysinternals)具有將數據寫入SQL服務器的功能。 網卡信息是一個字段,由CRLF CHAR(13)+ CHAR(10)分隔。 某些計算機可能只有一張網卡。 其他人可能有7個或更多的網卡。 每個字段(Network_Card,IP_Address和Subnet_Mask)都包含一個以CRLF分隔的匹配網卡信息列表。
我想將CRLF分隔的字段轉換為與Network_Card的第一項與IP_Address和Subnet_Mask的第一項匹配的多行。 然后,選擇Default_Gateway與IP_address匹配的記錄(指示哪個網卡處於活動狀態)。
BGInfo將許多字段填充到表中,對於本示例,我將使用所需的字段創建一個子集。
所以首先我們可以創建一個臨時的BGInfo表:
IF OBJECT_ID('tempdb..#BGInfo') IS NOT NULL DROP TABLE #BGInfo
CREATE TABLE #BGInfo (
[User_Name] nvarchar(25),
[Host_Name] nvarchar(25),
[Network_Card] nvarchar(MAX),
[IP_Address] nvarchar(255),
[Subnet_Mask] nvarchar(255),
[Default_Gateway] nvarchar(25)
)
然后用一些樣本數據填充它:
INSERT INTO #BGInfo
SELECT
'User1',
'PC-A',
'nic1'+CHAR(13)+CHAR(10)+'nic2'+CHAR(13)+CHAR(10)+'nic3'+CHAR(13)+CHAR(10)+'nic4'+CHAR(13)+CHAR(10)+'nic5'+CHAR(13)+CHAR(10)+'nic6'+CHAR(13)+CHAR(10)+'nic7',
'(none)'+CHAR(13)+CHAR(10)+'(none)'+CHAR(13)+CHAR(10)+'10.91.2.155'+CHAR(13)+CHAR(10)+'(none)'+CHAR(13)+CHAR(10)+'192.168.80.1'+CHAR(13)+CHAR(10)+'192.168.126.1'+CHAR(13)+CHAR(10)+'(none)',
'(none)'+CHAR(13)+CHAR(10)+'(none)'+CHAR(13)+CHAR(10)+'255.255.255.128'+CHAR(13)+CHAR(10)+'(none)'+CHAR(13)+CHAR(10)+'255.255.255.0'+CHAR(13)+CHAR(10)+'255.255.255.0'+CHAR(13)+CHAR(10)+'(none)',
'10.91.2.129'
UNION ALL
SELECT
'User2',
'PC-B',
'nic1'+CHAR(13)+CHAR(10)+'nic2'+CHAR(13)+CHAR(10)+'nic3'+CHAR(13)+CHAR(10)+'nic4'+CHAR(13)+CHAR(10)+'nic5'+CHAR(13)+CHAR(10)+'nic6'+CHAR(13)+CHAR(10)+'nic7',
'10.17.17.23'+CHAR(13)+CHAR(10)+'(none)'+CHAR(13)+CHAR(10)+'(none)'+CHAR(13)+CHAR(10)+'(none)'+CHAR(13)+CHAR(10)+'192.168.80.1'+CHAR(13)+CHAR(10)+'192.168.126.1'+CHAR(13)+CHAR(10)+'(none)',
'255.255.240.0'+CHAR(13)+CHAR(10)+'(none)'+CHAR(13)+CHAR(10)+'(none)'+CHAR(13)+CHAR(10)+'(none)'+CHAR(13)+CHAR(10)+'255.255.255.0'+CHAR(13)+CHAR(10)+'255.255.255.0'+CHAR(13)+CHAR(10)+'(none)',
'10.17.16.5'
我發現如果將CRLF轉換為逗號分隔值:
REPLACE([IP_Address],CHAR(13)+CHAR(10),',')
然后,我可以將其轉換為XML並選擇行。 (有關此方面的大量信息)但是...多列如何使用? 我已經嘗試過了,但是它只會創建很多重復的數據:
IF OBJECT_ID('tempdb..#XMLtable') IS NOT NULL DROP TABLE #XMLtable
CREATE TABLE #XMLtable (
[User_Name] nvarchar(25),
Computer nvarchar(25),
Network_Card nvarchar(255),
IPAddr xml,
Subnet xml,
Default_Gateway nvarchar(25)
)
INSERT INTO #XMLtable
SELECT
[User_Name],
[Host_Name] as Computer,
[Network_Card],
CAST('<i>'+REPLACE(REPLACE([IP_Address],CHAR(13)+CHAR(10),','),',','</i><i>')+'</i>' as xml) AS IPAddr,
CAST('<i>'+REPLACE(REPLACE([Subnet_Mask],CHAR(13)+CHAR(10),','),',','</i><i>')+'</i>' as xml) AS Subnet,
Default_Gateway
FROM #BGInfo bg
SELECT
[User_Name],
[Computer],
[Network_Card],
IP_Address = i.value('.', 'varchar(MAX)'),
IP_Address = s.value('.', 'varchar(MAX)'),
Default_Gateway
FROM #XMLtable
CROSS APPLY IPaddr.nodes ('/i') AS IPAddr(i)
CROSS APPLY Subnet.nodes ('/i') AS Subnet(s);
我想要一個類似這樣的列表:(數據可能不匹配,但您知道了)
User_Name Computer Network_Card IP_Address Subnet_Mask Default_Gateway
User1 PC-A nic1 (none) (none) 10.91.2.129
User1 PC-A nic2 (none) (none) 10.91.2.129
User1 PC-A nic3 10.91.2.155 255.255.255.128 10.91.2.129
User1 PC-A nic4 192.168.80.1 255.255.255.0 10.91.2.129
User1 PC-A nic5 192.168.126.1 255.255.255.0 10.91.2.129
User2 PC-B nic1 10.17.17.23 255.255.240.0 10.17.16.5
User2 PC-B nic2 (none) (none) 10.17.16.5
User2 PC-B nic3 (none) (none) 10.17.16.5
User2 PC-B nic4 192.168.80.1 255.255.255.0 10.17.16.5
User2 PC-B nic5 192.168.126.1 255.255.255.0 10.17.16.5
然后能夠選擇默認網關與IP_address匹配的ACTIVE網絡卡:
User_Name Computer Network_CardIP_Address Subnet_Mask Default_Gateway
User1 PC-A nic3 10.91.2.155 255.255.255.128 10.91.2.129
User2 PC-B nic1 10.17.17.23 255.255.240.0 10.17.16.5
我認為麻煩來自嘗試從單個#XMLtable表中分解卡,ip和子網。 使用與您相同的邏輯,我進行了三個單獨的查詢(每個查詢用於網卡,ips和子網),為每個查詢生成row_number,然后根據row_number將它們連接在一起。 然后,我使用PARSENAME
來比較IP地址和網關。
select nics.User_Name,
nics.Host_Name Computer,
nics.nic Network_Card,
ips.ip IP_Address,
subnets.subnet Subnet_Mask,
b.Default_Gateway
from
(
select User_Name, Host_Name,
nic.a.value('.', 'varchar(max)') as nic,
ROW_NUMBER() over (partition by User_name, Host_name order by User_name, Host_name) Row_number
from
(
select b.User_name, b.Host_name,
CAST('<i>' + REPLACE(b.Network_Card, CHAR(13)+CHAR(10), '</i><i>') + '</i>' as xml) as nics
from #BGInfo b
) a
cross apply nics.nodes('/i') as nic(a)
) nics
join
(
select User_Name, Host_Name,
ip.a.value('.', 'varchar(max)') as ip,
ROW_NUMBER() over (partition by User_name, Host_name order by User_name, Host_name) Row_number
from
(
select b.User_name, b.Host_name,
CAST('<i>' + REPLACE(b.IP_Address, CHAR(13)+CHAR(10), '</i><i>') + '</i>' as xml) as ips
from #BGInfo b
) a
cross apply ips.nodes('/i') as ip(a)
) ips on ips.User_Name = nics.User_Name and ips.Host_Name = nics.Host_Name and ips.Row_number = nics.Row_number
join
(
select User_Name, Host_Name,
subnet.a.value('.', 'varchar(max)') as subnet,
ROW_NUMBER() over (partition by User_name, Host_name order by User_name, Host_name) Row_number
from
(
select b.User_name, b.Host_name,
CAST('<i>' + REPLACE(b.Subnet_Mask, CHAR(13)+CHAR(10), '</i><i>') + '</i>' as xml) as subnets
from #BGInfo b
) a
cross apply subnets.nodes('/i') as subnet(a)
) subnets on subnets.User_Name = nics.User_Name and subnets.Host_Name = nics.Host_Name and subnets.Row_number = nics.Row_number
join #BGInfo b on b.User_Name = nics.User_Name and b.Host_Name = nics.Host_Name
where PARSENAME(ips.ip, 4) = PARSENAME(b.Default_Gateway, 4) and
PARSENAME(ips.ip, 3) = PARSENAME(b.Default_Gateway, 3)
我不知道它將如何針對大量的行執行,但是它適用於測試數據。
一種選擇是轉儲BGInfo,並使用PowerShell腳本。 Powershell可以獲取Nic信息,並且可以執行SQL。 如果您的台式機安裝了PowerShell,則可能更易於構建和維護,並且可能更快。 我發現有兩篇文章可以幫助您到達那里:
如果您必須使用BGInfo,那么就我個人而言,我將采用另一種方法,並分開關注點。
如果BGInfoTable上的唯一鍵是Time_stamp + User_name + Host_name,則可以有一個相關的nic表,如下所示:
CREATE TABLE BGInfo_Nics (
BGTime_Stamp DATETIME NOT NULL,
User_Name NVARCHAR(250) NOT NULL, -- these cols should match BGInfoTable datatypes
Host_Name NVARCHAR(250) NOT NULL,
NicName NVARCHAR(250) NOT NULL,
IPAddress NVARCHAR(50) NULL,
SubNet NVARCHAR(50) NULL,
GatewayIP NVARCHAR(50) NULL
)
GO
CREATE UNIQUE INDEX IX_BGInfo_Nics ON BGInfo_Nics ( BGTime_Stamp,
User_Name,
Host_Name,
NicName
)
GO
該程序將持續運行,並且每隔一段時間就會查詢BGInfoTable以查看是否有需要處理的行。 處理將包括從BGInfoTable讀取要處理的行,解析NIC信息(包括IP地址,子網和網關),並將nic信息行寫入新的(相關)表BGInfo_Nics。
例如:-用它來獲取需要處理的行:
SELECT bg.*
FROM BGInfoTable bg (NOLOCK) LEFT OUTER JOIN BGInfo_Nics nics (NOLOCK) ON
bg.Time_Stamp = nics.BGTime_Stamp AND
bg.[User_Name] = nics.[User_name] AND
bg.[Host_Name] = nics.[Host_name]
WHERE nics.BGTime_Stamp IS NULL
或者,您可以在BGInfoTable中添加幾列,以標記已經處理過的行,並創建索引以快速獲取未處理的行。
我有一個單獨的程序來處理(讀取新內容,解析和保存),我懷疑這種解決方案將在解析數據並將其保存為您真正想要報告的格式時提供更大的靈活性,並且效果會很好。
HTH
麥克風
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.