MS SQL 服务器 - 匹配表及其不相关的记录
MS SQL Server - Match tables and their unrelated records
这可能比我想象的更常见,但几个月来我一直在努力解决这个问题,但完全失败了。
两个 table 由完全不相关的系统提供相同的东西。但是他们对这些东西使用不同的标识符,我想找到一种方法将这些 tables 合并到一个中,其中 A 中的每个记录都将匹配它在 B 中的 "most equivalent" 记录。
两个数据库看起来都是这样的:
A.sessionID A.itemSeq A.dateSessionStart
1870 32 2017-12-20 16:00:00
1871 55 2017-12-23 17:00:00
1871 56 2017-12-23 17:00:00
1871 57 2017-12-23 17:00:00
1873 24 2018-01-05 15:00:00
1873 25 2018-01-05 15:00:00
1878 36 2018-01-12 15:30:00
B.sessionID B.itemID B.dateItem
520 1 2017-12-20 16:04:32
522 1 2017-12-23 17:15:04
522 2 2017-12-23 17:32:26
523 2 2018-01-05 15:41:06
523 3 2018-01-05 16:02:21
524 1 2018-01-12 15:37:55
这是table/view的一部分,我想获得:
A.sessionID B.sessionID A.itemSeq B.itemID A.dateSessionStart B.dateItem
1870 520 32 1 2017-12-20 16:00:00 2017-12-20 16:04:32
1871 522 55 1 2017-12-23 17:00:00 2017-12-23 17:15:04
1871 522 56 2 2017-12-23 17:00:00 2017-12-23 17:32:04
1871 522 57 NULL 2017-12-23 17:00:00 NULL
1873 523 24 2 2018-01-05 15:00:00 2018-01-05 15:41:06
1873 523 25 3 2018-01-05 15:00:00 2018-01-05 16:02:21
1878 524 36 1 2018-01-12 15:30:00 2018-01-12 15:37:55
似乎每个 table 中的日期和顺序可能允许一种相似的关系。然而:
- 不能保证 B 中有足够的记录来匹配 A 中的记录,反之亦然。这就是我试图通过 2018-12-23 在 B 中丢失的第三条记录来展示的内容。 A中也有一些"missing records"。实际上,两个 table 都有绝对疯狂日期的记录(我猜是免费输入)。
- 两者 table 都以某种方式使用顺序 ID,但规则不同。
我会将我在 SQL 查询中的知识描述为基础到中级。我试图在 SELECT 下创建一个子查询,通过 DATEDIFF 在两个 table 的日期时间之间进行匹配,在我所有的 SQL 书中寻找一个例子,在这里寻找类似的问题...但到目前为止我还没有成功。
谢谢!
法布里西奥·罗查
巴西利亚,巴西
您遇到的问题是您已将所有这些数据加载到关系数据库管理系统中;通过在 tables
之间明确定义关系来工作的设备
而且你们之间没有明确的关系
据我所知,你断言 "the order of the items from the sequence IDs is roughly the same from each system, and the times are approximately the same"
好吧,让我们假设这些系统都注意到播客何时出现,并且其中一个系统的时钟出现故障,该时钟仅报告小时,但除此之外,它是一个保持时间的工作时钟
看起来 B 的时钟比 A 的时钟快大约 3 分钟,因为您断言 b 的 16:02 等同于 a 的 15:00,但是 16:04 来自b 等同于 A
的 16:00
让我们把这些数据变成相关的东西table:
SELECT * FROM
(
SELECT
dateSessionStart,
ROW_NUMBER() OVER(PARTITION BY dateSessionStart ORDER BY sessionId, itemSeq) rn
FROM a
) a
FULL OUTER JOIN
(
SELECT
DATE_ADD(hour, HOUR(adj), CAST(CAST(adj as DATE) as DATETIME)) as dateSessionStart,
ROW_NUMBER() OVER(PARTITION BY CAST(adj as DATE), HOUR(adj) ORDER BY sessionid, itemid) rn
FROM(SELECT *, DATEADD(minute, -3, dateItem) adj FROM b) b
) b
ON a.dateSessionStart = b.dateSessionStart and a.rn = b.rn
如果您想要更多详细信息 运行 孤立的子查询,但基本上此查询将 b 记录的时间调整为 3 分钟,然后通过仅添加时间的小时来去除分钟和秒到日期(午夜)
单独但使用这些调整后的小时精度时间,使用 row_number 建立递增计数器。每次小时更改时,计数器都会从 1 重新开始。计数器按其他顺序 ID 的顺序递增。因此我们不关心序列 id 是否相等,我们只是使用它们来定义制作伪连接键的顺序。加入关系是建立在小时精确时间加上重新启动计数器
请注意,ghisb 是在未经测试的手机上编写的 - 可能存在小 typos/syntax 错误、缺少括号等 - 让我知道你在 运行 安装它时遇到的任何错误,我可以提供帮助出去了,但我今天很忙,所以请随意尝试一下,如果你修复了它们,建议修改我的答案
分区并不难;将它们视为自动连接回主数据的单独分组查询:
SELECT name, age, city, AVG(age) OVER(PARTITION BY city) FROM t
SELECT name, age, city, avgagecity
FROM
t
INNER JOIN
(SELECT city, AVG(age) as avgagecity FROM t GROUP BY city) c on c.city = t.city
以上两个查询在概念上是相同的;在一个中,我们按城市分组并平均年龄,然后将其加入主 table。瞧
另一个我们要求数据库按城市划分数据,平均年龄。数据库将隐式自动加入其 city:avgage 桶。当 t.city 是 "San Fran" 时,数据库会进入它的城市桶,寻找 san f运行,拉出平均年龄,将它贴在行
上
这里的 PARTITION BY 实际上是 GROUP BY x JOIN x
对于顺序很重要的事情,比如 row_number,一个 ORDER BY 是必要的,原因很明显。在这些类型的查询中,PARTITION BY 用于划分结果,但请注意,没有任何分组,因为 row_number 不是聚合操作。在有序操作(根本不需要分区)中,任何分区都指定何时重新开始,就好像数据在一个新的计数组中一样。其他几个常见的有序操作是 RANK 和 DENSE_RANK。像行号一样,但它们指的是获胜位置,因此值相等(一场比赛是 运行,一个人在 3:59 中赢得了一英里,然后两个人在 4 分钟内 运行 一英里。他们是 运行ked equal 2nd. 如果有人并列,RANK 跳过数字,所以 1st,等于 2nd 2nd,4th(没有第 3 名,因为并列第 2 名而被跳过。如果 3 个人并列, 4 第 4 名将跳过等)。DENSE_RANK 不会跳过
这可能比我想象的更常见,但几个月来我一直在努力解决这个问题,但完全失败了。
两个 table 由完全不相关的系统提供相同的东西。但是他们对这些东西使用不同的标识符,我想找到一种方法将这些 tables 合并到一个中,其中 A 中的每个记录都将匹配它在 B 中的 "most equivalent" 记录。
两个数据库看起来都是这样的:
A.sessionID A.itemSeq A.dateSessionStart
1870 32 2017-12-20 16:00:00
1871 55 2017-12-23 17:00:00
1871 56 2017-12-23 17:00:00
1871 57 2017-12-23 17:00:00
1873 24 2018-01-05 15:00:00
1873 25 2018-01-05 15:00:00
1878 36 2018-01-12 15:30:00
B.sessionID B.itemID B.dateItem
520 1 2017-12-20 16:04:32
522 1 2017-12-23 17:15:04
522 2 2017-12-23 17:32:26
523 2 2018-01-05 15:41:06
523 3 2018-01-05 16:02:21
524 1 2018-01-12 15:37:55
这是table/view的一部分,我想获得:
A.sessionID B.sessionID A.itemSeq B.itemID A.dateSessionStart B.dateItem
1870 520 32 1 2017-12-20 16:00:00 2017-12-20 16:04:32
1871 522 55 1 2017-12-23 17:00:00 2017-12-23 17:15:04
1871 522 56 2 2017-12-23 17:00:00 2017-12-23 17:32:04
1871 522 57 NULL 2017-12-23 17:00:00 NULL
1873 523 24 2 2018-01-05 15:00:00 2018-01-05 15:41:06
1873 523 25 3 2018-01-05 15:00:00 2018-01-05 16:02:21
1878 524 36 1 2018-01-12 15:30:00 2018-01-12 15:37:55
似乎每个 table 中的日期和顺序可能允许一种相似的关系。然而:
- 不能保证 B 中有足够的记录来匹配 A 中的记录,反之亦然。这就是我试图通过 2018-12-23 在 B 中丢失的第三条记录来展示的内容。 A中也有一些"missing records"。实际上,两个 table 都有绝对疯狂日期的记录(我猜是免费输入)。
- 两者 table 都以某种方式使用顺序 ID,但规则不同。
我会将我在 SQL 查询中的知识描述为基础到中级。我试图在 SELECT 下创建一个子查询,通过 DATEDIFF 在两个 table 的日期时间之间进行匹配,在我所有的 SQL 书中寻找一个例子,在这里寻找类似的问题...但到目前为止我还没有成功。
谢谢!
法布里西奥·罗查
巴西利亚,巴西
您遇到的问题是您已将所有这些数据加载到关系数据库管理系统中;通过在 tables
之间明确定义关系来工作的设备而且你们之间没有明确的关系
据我所知,你断言 "the order of the items from the sequence IDs is roughly the same from each system, and the times are approximately the same"
好吧,让我们假设这些系统都注意到播客何时出现,并且其中一个系统的时钟出现故障,该时钟仅报告小时,但除此之外,它是一个保持时间的工作时钟
看起来 B 的时钟比 A 的时钟快大约 3 分钟,因为您断言 b 的 16:02 等同于 a 的 15:00,但是 16:04 来自b 等同于 A
的 16:00让我们把这些数据变成相关的东西table:
SELECT * FROM
(
SELECT
dateSessionStart,
ROW_NUMBER() OVER(PARTITION BY dateSessionStart ORDER BY sessionId, itemSeq) rn
FROM a
) a
FULL OUTER JOIN
(
SELECT
DATE_ADD(hour, HOUR(adj), CAST(CAST(adj as DATE) as DATETIME)) as dateSessionStart,
ROW_NUMBER() OVER(PARTITION BY CAST(adj as DATE), HOUR(adj) ORDER BY sessionid, itemid) rn
FROM(SELECT *, DATEADD(minute, -3, dateItem) adj FROM b) b
) b
ON a.dateSessionStart = b.dateSessionStart and a.rn = b.rn
如果您想要更多详细信息 运行 孤立的子查询,但基本上此查询将 b 记录的时间调整为 3 分钟,然后通过仅添加时间的小时来去除分钟和秒到日期(午夜)
单独但使用这些调整后的小时精度时间,使用 row_number 建立递增计数器。每次小时更改时,计数器都会从 1 重新开始。计数器按其他顺序 ID 的顺序递增。因此我们不关心序列 id 是否相等,我们只是使用它们来定义制作伪连接键的顺序。加入关系是建立在小时精确时间加上重新启动计数器
请注意,ghisb 是在未经测试的手机上编写的 - 可能存在小 typos/syntax 错误、缺少括号等 - 让我知道你在 运行 安装它时遇到的任何错误,我可以提供帮助出去了,但我今天很忙,所以请随意尝试一下,如果你修复了它们,建议修改我的答案
分区并不难;将它们视为自动连接回主数据的单独分组查询:
SELECT name, age, city, AVG(age) OVER(PARTITION BY city) FROM t
SELECT name, age, city, avgagecity
FROM
t
INNER JOIN
(SELECT city, AVG(age) as avgagecity FROM t GROUP BY city) c on c.city = t.city
以上两个查询在概念上是相同的;在一个中,我们按城市分组并平均年龄,然后将其加入主 table。瞧
另一个我们要求数据库按城市划分数据,平均年龄。数据库将隐式自动加入其 city:avgage 桶。当 t.city 是 "San Fran" 时,数据库会进入它的城市桶,寻找 san f运行,拉出平均年龄,将它贴在行
上这里的 PARTITION BY 实际上是 GROUP BY x JOIN x
对于顺序很重要的事情,比如 row_number,一个 ORDER BY 是必要的,原因很明显。在这些类型的查询中,PARTITION BY 用于划分结果,但请注意,没有任何分组,因为 row_number 不是聚合操作。在有序操作(根本不需要分区)中,任何分区都指定何时重新开始,就好像数据在一个新的计数组中一样。其他几个常见的有序操作是 RANK 和 DENSE_RANK。像行号一样,但它们指的是获胜位置,因此值相等(一场比赛是 运行,一个人在 3:59 中赢得了一英里,然后两个人在 4 分钟内 运行 一英里。他们是 运行ked equal 2nd. 如果有人并列,RANK 跳过数字,所以 1st,等于 2nd 2nd,4th(没有第 3 名,因为并列第 2 名而被跳过。如果 3 个人并列, 4 第 4 名将跳过等)。DENSE_RANK 不会跳过