LEFT JOIN 两个 MySql 表,而第二个表按 desc 排序并限制为 1

Posted

技术标签:

【中文标题】LEFT JOIN 两个 MySql 表,而第二个表按 desc 排序并限制为 1【英文标题】:LEFT JOIN two MySql tables while second table order by desc and limit to 1 【发布时间】:2020-08-06 13:07:33 【问题描述】:

我有两张如下表

NA_table
+----+-------+-------+---------------+---------------------+-----+
| id | nname  | phone |      nip       |        ntime      | iid |
+----+-------+-------+---------------+---------------------+-----+
|  1 | john  | +xxxx |  192.168.1.10 | 2020-04-21 11:10:10 |  23 |
|  2 | bill  | +xxxx |  192.168.1.11 | 2020-04-21 12:10:10 |  44 |
|  3 | husky | +xxxx |  192.168.1.12 | 2020-04-21 13:10:10 |  44 |
|  4 | lab   | +xxxx |  192.168.1.13 | 2020-04-21 14:10:10 |  33 |
|  5 | bill  | +xxxx |  192.168.1.12 | 2020-04-21 11:10:15 |  44 |
|  6 | cal   | +xxxx |  192.168.1.13 | 2020-04-21 16:10:10 |  12 |
|  7 | jess  | +xxxx |  192.168.1.11 | 2020-04-21 17:10:10 |  90 |
|  8 | minn  | +xxxx |  192.168.1.12 | 2020-04-21 18:10:10 |  44 |
|  9 | jess  | +xxxx |  192.168.1.11 | 2020-04-21 17:10:10 |  21 |
+----+-------+-------+---------------+---------------------+-----+


CD_table
+----+--------------+---------------------+-------+
| cid |     cip      |       ctime        | other |
+----+--------------+---------------------+-------+
|  1 | 192.168.1.11 | 2020-04-21 03:22:19 | text  |
|  2 | 192.168.1.12 | 2020-04-21 03:10:10 | text  |
|  3 | 192.168.1.11 | 2020-04-21 06:11:12 | text  |
|  4 | 192.168.1.19 | 2020-04-21 06:10:03 | text  |
|  5 | 192.168.1.22 | 2020-04-21 13:10:10 | text  |
|  6 | 192.168.1.11 | 2020-04-21 14:14:17 | text  |
|  7 | 192.168.1.12 | 2020-04-21 16:09:10 | text  |
|  8 | 192.168.1.11 | 2020-04-22 09:07:11 | text  |
+----+--------------+---------------------+-------+

使用这两个表我想运行这个查询

SELECT
CD_table.ctime AS CTIME,
CD_table.cip AS CIP,
CD_table.other AS OTHER,
NA_table.phone AS PHONE,
FROM NA_table
LEFT JOIN CD_table 
ON NA_table.nip = CD_table.cip 
WHERE
NA_table.NAIID = '44'
AND
NA_table.ntime between '2020-04-21 11:10:00' AND '2020-04-21 11:10:59'
AND
CD_table.ctime between '2020-04-21 00:10:00' AND '2020-04-21 23:59:59'

这给了我这个结果。

+----------------------+---------------+-------+-------+
|        CTIME         |      CIP      | OTHER | PHONE |
+----------------------+---------------+-------+-------+
| 2020-04-21 03:22:19  |  192.168.1.11 | text  | +xxxx |
| 2020-04-21 03:10:10  |  192.168.1.12 | text  | +xxxx |
| 2020-04-21 06:11:12  |  192.168.1.11 | text  | +xxxx |
| 2020-04-21 14:14:17  | 192.168.1.11  | text  | +xxxx |
| 2020-04-21 16:09:10  |  192.168.1.12 | text  | +xxxx |
+----------------------+---------------+-------+-------+

但我希望我的输出按 CTIME 排序,并且只打印每个 CD_table 记录的最后一个匹配记录,像这样

+---------------------+---------------+-------+-------+
|        CTIME        |      CIP      | OTHER | phone |
+---------------------+---------------+-------+-------+
| 2020-04-21 14:14:17 | 192.168.1.11  | text  | +xxxx |
| 2020-04-21 16:09:10 |  192.168.1.12 | text  | +xxxx |
+---------------------+---------------+-------+-------+

我可以在哪里执行 DESC 和 LIMIT 部分的查询。或者还有其他方法可以拆分我的查询。

【问题讨论】:

of the each CD_table record 嗯? 我的意思是我需要显示每个 CIP 的最高时间。像 CIP 192.168.1.12 应该只出现一次,时间为 2020-04-21 16:09:10 好吧,你原来的结果并没有反映那个意图 【参考方案1】:

您可以使用子查询过滤cd_table,以获取给定时间间隔内的最新记录。

另外,由于您的查询没有从 na_table 返回任何内容,因此我将连接转换为 exists 条件 - 在这种情况下这通常更有效。

select c.ctime, c.cip, c.other
from cd_table c
where 
    c.ctime = (
        select max(c1.ctime) 
        from cd_table c1 
        where 
            c1.cip = c.cip
            and c1.ctime >= '2020-04-21 00:10:00'
            and c1.ctime <  '2020-04-22'
    )
    and exists (
        select 1 
        from na_table n
        where 
            n.nip = c.cip
            and n.naid = 44 
            and n.ntime >= '2020-04-21 11:10:00'
            and n.ntime <  '2020-04-21 11:11:00'
    )

请注意,我重写了日期条件以使用半开间隔(这样可以避免每次都处理尾随 59 秒)。

为了性能,请考虑以下指标:

cd_table(cip, ctime)
na_table(nip, naid, time)

other 添加到cd_table 的索引可能会带来额外的提升。

【讨论】:

您好,感谢您的快速回复。在 n.nip = c.cip 行上删除“AND”后,我测试了你的 sql。似乎需要永远执行并获得结果。我用少于 1M 行的新数据库进行了测试。仍然没有改善 @gripenfighter:你有两个推荐的索引吗? 可能是因为我选择的电话号码列也来自 NA_table ?我看到你使用存在,因为我的问题没有提到 NA_table 中的任何列 对不起,我用额外的专栏再次更新了我的问题【参考方案2】:

如果您使用的是mysql 8.0,那么您可以使用row_number()

SELECT
    CTIME,
    CIP,
    OTHER
from
(
    SELECT
    CD_table.ctime AS CTIME,
    CD_table.cip AS CIP,
    CD_table.other AS OTHER
    row_number() over (order by CTIME desc) as rnk
    FROM NA_table
    LEFT JOIN CD_table 
    ON NA_table.nip = CD_table.cip 
    WHERE
    NA_table.NAIID = '44'
    AND
    NA_table.ntime between '2020-04-21 11:10:00' AND '2020-04-21 11:10:59'
    AND
    CD_table.ctime between '2020-04-21 00:10:00' AND '2020-04-21 23:59:59'
) val
where rnk = 1
order by
    CTIME

【讨论】:

以上是关于LEFT JOIN 两个 MySql 表,而第二个表按 desc 排序并限制为 1的主要内容,如果未能解决你的问题,请参考以下文章

带left join 的sql的执行顺序

MySQL LEFT JOIN 不能处理多个表

MySQL LEFT JOIN 在第二个表中具有可选值

LEFT JOIN,如果存在第二个表的结果,则添加到 PHP 中的数组

MySQL 数据库中 left outer join 和 left join 啥区别?

MYSQL - 仅当 LEFT JOIN 中的行不存在时才选择