MySQL - 向左连接添加子句

Posted

技术标签:

【中文标题】MySQL - 向左连接添加子句【英文标题】:MySQL - add clauses to left join 【发布时间】:2017-05-01 15:50:33 【问题描述】:

我有一个名为属性 (p) 的表和另一个名为证书 (c) 的表。可以为每个财产分配一个以上的证书,或者根本没有证书。我需要生成一个使用连接的查询,并且每个属性只显示证书表中的一个证书。显示的一份证书必须是具有最近到期日期的证书。证书表中有一个名为“certificate_expiry_date”的字段。简单的连接是 p.property_id = c.certificate_property 但这当前会输出所有证书。

我的查询尝试

这是我目前的查询;

SELECT DISTINCT t.tenancy_property, t.*, p.*, c.* FROM tenancy t
INNER JOIN property p
on t.tenancy_property = p.property_id
LEFT JOIN
(
    SELECT *
    FROM certificate
    WHERE certificate_expiry_date > CURDATE()
    ORDER BY certificate_expiry_date DESC
    LIMIT 1
) c ON p.property_id = c.certificate_property
WHERE t.tenancy_type='1' AND p.property_mains_gas_supply='1' AND p.property_availability='2' ORDER BY t.tenancy_id DESC LIMIT $startpoint , $per_page

此查询执行良好,但似乎没有考虑证书表上的左连接。

certificate的表结构

CREATE TABLE IF NOT EXISTS `certificate` (
  `certificate_id` int(11) NOT NULL AUTO_INCREMENT,
  `certificate_property` int(11) DEFAULT NULL,
  `certificate_type` tinyint(4) DEFAULT NULL,
  `certificate_reference` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `certificate_start_date` date DEFAULT NULL,
  `certificate_expiry_date` date DEFAULT NULL,
  `certificate_notes` text COLLATE utf8_bin,
  `certificate_renewal_instructed` tinyint(4) DEFAULT NULL,
  `certificate_renewal_contractor` int(11) DEFAULT NULL,
  PRIMARY KEY (`certificate_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=219 ;

【问题讨论】:

经典“每组选择前 n 个”。 我添加了greatest-n-per-group 标签。这个问题已经回答了数百次。只需点击标签链接即可。 @BillKarwin 他只选择了一项,所以没有组。 @Barmar:比尔是对的。 OP 描述了从与属性相关的一组证书中获取单个“最新”证书。这是一个classic.latest-n-per-group。 @BillKarwin 我需要每个房产的最新证书 【参考方案1】:

用您的更新信息更新答案

这样的?

(注意:此答案假设每个属性至少有一个证书,否则子查询 qMostRecentExpire 可能会失败)

select
    p.property_id
    , p.* 
    , ( select 
            c.certificate_id 
        from
            certificates    as c 
        where
            c.certificate_property = p.property_id    -- all the cert of this property 
            and c.certificate_expiry_date < CURDATE() -- cert has expired
        order by c.certificate_expiry_date desc       
        limit 1                                       -- most recent one 
      ) as qMostRecentExpire
from
    properties  as p

知道某些属性可能没有证书后更新答案

select
    p.property_id
    , p.* 
    , ( select 
            c.certificate_id 
        from
            certificates    as c 
        where
            c.certificate_property = p.property_id    -- all the cert of this property 
            and c.certificate_expiry_date < CURDATE() -- cert has expired
        order by c.certificate_expiry_date desc       
        limit 1                                       -- most recent one 
    ) as qMostRecentExpire
from
    properties      as p
    , certificates  as c                              -- inner join : properties that
where                                                 -- has not cert will be dropped
    p.property_id = c.certificate_property            

【讨论】:

请查看我帖子的更新,它可能会帮助您改进答案 我在上面更新了我的答案。我没有你的表,所以我无法测试我的查询。但它可能会给你一些想法。祝你好运! 感谢您的努力,但很遗憾,不能保证每家酒店都至少拥有一个证书 好的,我再次更新了我的答案,以说明您的规范,即某些属性可能没有证书。【参考方案2】:

如果我们只需要从certificates 表中返回一两列,我们有时可以在 SELECT 列表中使用相关子查询。

这种方法对大型表有一些性能影响;但对于某些用例,如果有适当的索引可用,这可能是一种可行的方法。

 SELECT p.id
      , p.somecol

      , ( SELECT c.col
            FROM certificate c
           WHERE c.property_id = p.id
           ORDER BY c.date_added DESC, c.id DESC
           LIMIT 1
        ) AS most_recent_cert_col

      , ( SELECT c.date_added
            FROM certificate c
           WHERE c.property_id = p.id
           ORDER BY c.date_added DESC, c.id DESC
           LIMIT 1
        ) AS most_recent_cert_date_added

   FROM property p 
  WHERE ... 
 ORDER BY ... 

【讨论】:

请查看我帖子的更新。额外的信息是否有助于以任何方式改善您的答案?我需要从证书表中返回所有列 @MichaelLB:如果您需要返回certificate 表中的所有列,那么正如比尔·卡尔文在评论中指出的那样,这是一个熟悉的问题,已被回答 几十次 次。

以上是关于MySQL - 向左连接添加子句的主要内容,如果未能解决你的问题,请参考以下文章

左连接的where子句中的Mysql查询错误

mysql 查询 - 使用左连接和 where 子句的多个计数

使用左连接在 from 子句上添加表

mysql连接内连接左连接右连接全连接

MySQL 5 左连接未知列

MySql:查询优化(多个左连接)