【问题标题】:How to join two tables based on non-unique id in the first table (SQLite 3.12.2)如何根据第一个表中的非唯一 id 连接两个表(SQLite 3.12.2)
【发布时间】:2021-11-17 10:10:42
【问题描述】:

表 A 的序列号是随机生成的。 然后表 B 会根据表 A 的序列号和类别来生成它的声音。

我试过这个命令,但失败了。

SELECT          A.timestamp, B.timestamp, B.SerialID, A.Category, B.Sound
FROM            A 
JOIN            B
ON              B.timestamp > A.timestamp
AND             A.SerialID = B.SerialID
ORDER BY        B.timestamp

表 A

Timestamp SerialID Category
1 3 Cat
2 5 Dog
10 44 Cat
13 5 Cat
15 3 Dog

表 B

Timestamp SerialID Sound
3 3 Meow
4 5 Bark
11 44 Meow
14 5 Meow
16 3 Bark

Desire表:表B的第一个匹配表A和B的序列id。Timestamp大于A.Timestamp

A.Timestamp B.Timestamp Serial ID Category Sound
1 3 3 Cat Meow
2 4 5 Dog Bark
10 11 44 Cat Meow
13 14 5 Cat Meow
15 16 3 Dog Bark

【问题讨论】:

  • 你的 MySQL 版本是多少?最简单的解决方案可能是带有限制子句的横向连接。自 MySQL 8.0.14 以来,横向连接就已成为特色。
  • 抱歉添加错误信息,是SQLite 3.12.2

标签: sql sqlite


【解决方案1】:
SELECT
   t1.Timestamp AS TimestampA,
   t2.Timestamp AS TimestampB,
   t1.SerialID,
   t1.Category,
   t2.Sound
FROM t1
JOIN t2 ON t1.SerialID = t2.SerialID AND t2.Timestamp = (
  SELECT MIN(t2.Timestamp)
  FROM t2 
  WHERE t2.SerialID = t1.SerialID AND t2.Timestamp >= t1.Timestamp
)

或者同样使用LEAD窗口函数

WITH cte AS (
  SELECT *,
    LEAD(Timestamp) OVER (PARTITION BY SerialID ORDER BY Timestamp) as next_timestamp
  FROM t1
)
SELECT
   t1.Timestamp AS TimestampA,
   t2.Timestamp AS TimestampB,
   t1.SerialID,
   t1.Category,
   t2.Sound
FROM cte t1
JOIN t2 ON t1.SerialID = t2.SerialID 
       AND t2.Timestamp >= t1.Timestamp 
       AND (t2.Timestamp < t1.next_timestamp OR next_timestamp IS NULL)
ORDER BY t1.Timestamp  

db<>fiddle

【讨论】:

  • 我喜欢这两种方法。将条件放在ON 子句中非常简单,LEAD 将其作为我们正在寻找匹配的时间戳范围的想法是很棒的想法。
【解决方案2】:

我最喜欢的解决方案是横向连接,只需为每个 A 行选择所需的 B 行。但是 SQLite 中还没有横向连接。

您可以通过两个步骤获得所需的 B 行,方法是在 select 子句的子查询中选择 tmestamp:

select
  ab.timestamp as a_timestamp, b.timestamp as b_timestamp,
  b.serialid, ab.category, b.sound
from
(
  select a.*,
    (
      select b.timestamp
      from b
      where b.serialid = a.serialid
      and b.timestamp > a.timestamp
      order by b.timestamp
      limit 1
    ) as best_timestamp
  from a
) ab
join b on b.serialid = ab.serialid
      and b.timestamp = ab.best_timestamp
order by ab.timestamp, ab.serialid;

另一种方法使用窗口函数。在那里,您将加入所有候选人,然后保留最好的候选人。这是您添加了选择的查询。

select a_timestamp, b_timestamp, serialid, category, sound
from
(
  select 
    a.timestamp as a_timestamp, b.timestamp as b_timestamp,
    b.serialid, a.category, b.sound,
    min(b.timestamp) over (partition by a.serialid) as best_timestamp
  from a
  join b on b.serialid = a.serialid and b.timestamp > a.timestamp
) ab
where b_timestamp = best_timestamp
order by a_timestamp, ab.serialid;

【讨论】:

    【解决方案3】:

    您只需要一个连接和聚合,而不需要子查询:

    SELECT a.Timestamp a_Timestamp,
           MIN(b.Timestamp) b_Timestamp,
           a.SerialID,
           a.Category,
           b.Sound
    FROM TableA a INNER JOIN TableB b
    ON b.SerialID = a.SerialID AND b.Timestamp > a.Timestamp
    GROUP BY a.Timestamp, a.SerialID;
    

    当使用MIN()聚合函数时,此代码依赖SQLite的特性来返回包含列最小值的行。

    如果TimestampSerialID 的组合在TableA 中不是唯一的,则更改为:

    GROUP BY a.Timestamp, a.SerialID, a.Category 
    

    请参阅demo

    【讨论】:

    • 我喜欢这个答案,它要快得多!
    猜你喜欢
    • 2022-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多