簡體   English   中英

T-SQL從多個由回車符分隔的列中選擇匹配的行(BGInfo輸出)

[英]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)

我不知道它將如何針對大量的行執行,但是它適用於測試數據。

SQL小提琴

一種選擇是轉儲BGInfo,並使用PowerShell腳本。 Powershell可以獲取Nic信息,並且可以執行SQL。 如果您的台式機安裝了PowerShell,則可能更易於構建和維護,並且可能更快。 我發現有兩篇文章可以幫助您到達那里:

  1. 獲取NIC信息: http : //techibee.com/powershell/powershell-get-ip-address-subnet-gateway-dns-serves-and-mac-address-details-of-remote-computer/1367
  2. 保存到數據庫: http : //www.sqlservercentral.com/Forums/Topic1463926-1351-1.aspx

如果您必須使用BGInfo,那么就我個人而言,我將采用另一種方法,並分開關注點。

  1. 允許BGInfo以最簡單,最直接的方式(即將多個IP地址填充到一個定界的列中)寫入SQL
  2. 創建一個相關的表,其中將包含已解析的nic + IP地址信息。
  3. 創建一個簡單的程序(不是SQL,更像是C#),例如BGInfo表中未處理的行,將nic + IP地址信息解析到相關表中。

如果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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM