【问题标题】:Join two rows based on two different ids根据两个不同的 id 连接两行
【发布时间】:2020-11-25 07:46:29
【问题描述】:

我目前正在尝试连接从我们的系统日志服务器加载的 SQL Server 上的表中的行。该表列出了我们用户的 VPN 会话,并且始终包含会话的两行:会话开始时记录事件 2201,会话终止时记录事件 2202。

+---------+---------+----------+------ ------------+-------------+----------+------------+ |事件日期时间 |事件ID |用户名 |源代码 |虚拟IP |接收 |交易 | +---------+---------+----------+------ ------------+-------------+----------+------------+ | 2020-11-24 06:22:05 | 2201 | abc1234 | 111.222.333.444 | 172.16.10.2 |空 |空 | | 2020-11-24 13:15:17 | 2201 | abc1234 | 111.222.333.444 | 172.16.10.4 |空 |空 | | 2020-11-24 13:15:27 | 2202 | abc1234 | 111.222.333.444 | 172.16.10.2 | 74411108 | 655423421 | | 2020-11-24 14:40:45 | 2202 | abc1234 | 111.222.333.444 | 172.16.10.4 | 22629095 | 175651830 | | 2020-11-24 20:07:53 | 2201 | abc1234 | 111.222.333.444 | 172.16.10.2 |空 |空 | | 2020-11-24 20:11:45 | 2202 | abc1234 | 111.222.333.444 | 172.16.10.2 | 1086188 | 5786362 | | 2020-11-21 10:39:59 | 2201 | def5678 | 55.66.77.88 | 172.16.10.2 |空 |空 | | 2020-11-21 11:26:49 | 2202 | def5678 | 55.66.77.88 | 172.16.10.2 | 25364309 | 40329362 | | 2020-11-22 10:07:51 | 2201 | def5678 | 55.66.77.88 | 172.16.10.2 |空 |空 | | 2020-11-22 10:33:15 | 2202 | def5678 | 55.66.77.88 | 172.16.10.2 | 7929825 | 34662648 | +---------+---------+----------+------ ------------+-------------+----------+------------+ CREATE TABLE syslog ( eventdatetime datetime NOT NULL, eventid smallint NOT NULL, username varchar(20) NOT NULL, srcip varchar(15) NOT NULL, virtual_ip varchar(15) NOT NULL, rx int NULL, tx int NULL )

但是,我不能只按日期和时间对表格进行排序,因为用户可以在不同的设备上创建多个会话。上表显示了前四行中的此类事件。 会话的用户名、srcip 和 virtual_ip 的值相同,但我将有两个时间戳和两个事件 ID。

如何将会话中的这两行组合起来,以便得到这样的输出:

+------------------+----------+------ -----+-----------------+-------------+----------+- ----------+ |开始 |结束 |用户名 |源代码 |虚拟IP |接收 |交易 | +------------------+----------+------ -----+-----------------+-------------+----------+- ----------+ | 2020-11-24 06:22:05 | 2020-11-24 13:15:27 | abc1234 | 111.222.333.444 | 172.16.10.2 | 74411108 | 655423421 | | 2020-11-24 13:15:17 | 2020-11-24 14:40:45 | abc1234 | 111.222.333.444 | 172.16.10.4 | 22629095 | 175651830 | | 2020-11-21 10:39:59 | 2020-11-21 11:26:49 | def5678 | 55.66.77.88 | 172.16.10.2 | 7929825 | 34662648 | +------------------+----------+------ -----+-----------------+-------------+----------+- ----------+

【问题讨论】:

  • 根据您的示例,每个会话 aof 用户 abc1234 有三个事件
  • 但是,我建议使用 cte 来创建行号,例如 ROW_NUMBER() OVER (PARTITION BY username, srcip, virtual_ip ORDER BY eventdatetime) - 之后您可以使用 rn = 1 从您的 cte 中选择所有行,然后使用rn 2(或类似,因为在您的示例中,用户 def5678 的 rn 将达到 4)。
  • 您的表中没有设备标识符吗?
  • @Tyron78 是的,正确,两个重叠了几秒钟,第三个重叠了。
  • @PeterSmith 不幸的是没有。来自 Syslog 服务器的行是从我们的防火墙生成的,我无法更改输出。整行如下所示:id="2201" severity="info" sys="SecureNet" sub="vpn" event="Connection started" username="abc1234" variant="ssl" srcip="111.222.333.444" virtual_ip="172.16.10.2"

标签: sql-server tsql


【解决方案1】:
SELECT [Start]
      ,[End]
      ,[username]
      ,[srcip]
      ,[virtual_ip]
      ,rx
      ,tx
FROM( 
SELECT [eventdatetime] AS [Start]
      ,LEAD(eventdatetime) OVER (ORDER BY [username], virtual_ip, eventdatetime) AS [End]
      ,[eventid]
      ,[username]
      ,[srcip]
      ,[virtual_ip]
      ,LEAD([rx]) OVER (ORDER BY [username], virtual_ip, eventdatetime) rx
      ,LEAD([tx]) OVER (ORDER BY [username], virtual_ip, eventdatetime) tx
  FROM [Test].[dbo].[syslog]) A
  WHERE eventid =2201
  ORDER BY[username], virtual_ip, [End]

【讨论】:

  • 不错!但是 srcip 是否也应该包含在 WindowFunctions 的 ORDER BY 中?毕竟,用户连接多个设备可能会发生......
【解决方案2】:

我几乎掌握了 Tyron78 所说的内容,只是做了一些小改动。就我而言,我使用了 join 表 syslog 到表 syslog 可能不是最佳选择,您可以改用嵌套查询,或将 cte 拆分为两个查询。

这是我的代码:

USE Test
GO

WITH cte AS (
    SELECT
        ROW_NUMBER() OVER (
            PARTITION BY 
                [Start].[username], 
                [Start].[srcip], 
                [Start].[virtual_ip]
            ORDER BY 
                [Start].[eventdatetime]
        ) [row_num],
        [Start].[eventdatetime] AS [start],
        [End].[eventdatetime] AS [end],
        [Start].[username],
        [Start].[srcip],
        [Start].[virtual_ip],
        [End].[rx],
        [End].[tx]
    FROM [syslog] AS [Start]
        INNER JOIN [syslog] AS [End] ON [Start].[username] = [End].[username]
            AND [Start].[srcip] = [End].[srcip]
            AND [Start].[virtual_ip] = [End].[virtual_ip]
            AND [End].[eventid] = 2202
    WHERE [Start].[eventid] = 2201
)
SELECT [start], [end], [username], [srcip], [virtual_ip], [rx], [tx] FROM cte WHERE row_num = 1

此外,此代码可以移动到视图中。

【讨论】:

  • 这不会返回所有行:我当前的示例表中有 310 行,而 @Sai 的解决方案返回正确的数量 155,CTE 解决方案只返回 52。似乎条目是分组到每个用户和每天的第一个 IP。如果用户多次连接并重复使用 IP,则不会显示行。如果我将 [Start].[eventdatetime] 或 [End].[eventdatetime] 添加到 PARTITION BY 子句,我会得到正确数量的行,但是开始时间或结束时间会重复。
猜你喜欢
  • 2019-12-18
  • 1970-01-01
  • 1970-01-01
  • 2021-03-30
  • 1970-01-01
  • 2021-12-22
  • 2016-11-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多