如何在嵌套子查询连接中选择最后一个N?

时间:2016-02-05 02:39:22

标签: mysql greatest-n-per-group

我很熟悉从子组或表(for example)中查找最后一项,但我现在尝试使用嵌套连接对第二层执行相同的操作。

考虑以下架构和数据:

/*
SQLyog Ultimate v11.52 (64 bit)
MySQL - 5.6.17 : Database - test
*********************************************************************
*/

/*Table structure for table `companies` */

CREATE TABLE `companies` (
  `company_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `company_name` varchar(16) DEFAULT NULL,
  PRIMARY KEY (`company_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;

/*Data for the table `companies` */

insert  into `companies`(`company_id`,`company_name`) values (1,'Company1'),(2,'Company2');

/*Table structure for table `employees` */

CREATE TABLE `employees` (
  `employee_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` int(10) unsigned NOT NULL,
  `employee_name` varchar(16) NOT NULL,
  PRIMARY KEY (`employee_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;

/*Data for the table `employees` */

insert  into `employees`(`employee_id`,`company_id`,`employee_name`) values (1,1,'Employee1'),(2,1,'Employee2'),(3,2,'Employee3'),(4,2,'Employee4');

/*Table structure for table `orders` */

CREATE TABLE `orders` (
  `order_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `employee_id` int(10) unsigned NOT NULL,
  `order_date` datetime NOT NULL,
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;

/*Data for the table `orders` */

insert  into `orders`(`order_id`,`employee_id`,`order_date`) values (1,1,'2016-02-01 00:00:00'),(2,1,'2016-02-02 00:00:00'),(3,2,'2016-02-03 00:00:00'),(4,2,'2016-01-28 00:00:00'),(5,3,'2016-01-29 00:00:00'),(6,3,'2016-01-30 00:00:00'),(7,4,'2016-01-31 00:00:00'),(8,4,'2016-01-27 00:00:00');

回答问题“每个员工的最后订单是什么?”是一个相对简单的问题:

SELECT c.company_name, e.employee_name, last_order.order_id, last_order.order_date
FROM employees e
JOIN companies c ON c.company_id = e.company_id
LEFT JOIN
(
    SELECT o1.employee_id, o1.order_id, o1.order_date
    FROM orders o1
    LEFT JOIN orders o2 ON (o1.employee_id = o2.employee_id AND o1.order_date < o2.order_date)
    WHERE o2.order_id IS NULL
) AS last_order ON last_order.employee_id = e.employee_id;

-- output ===================================================

company_name  employee_name  order_id  order_date           
Company1      Employee1      2         2016-02-02 00:00:00  
Company1      Employee2      3         2016-02-03 00:00:00  
Company2      Employee3      6         2016-01-30 00:00:00  
Company2      Employee4      7         2016-01-31 00:00:00   

但现在我需要问“每个公司的最后订单是什么?”,其中订单是另一层(companies > employees > orders)。< / p>

我一直在试图弄清楚如何构建一个查询,允许我使用已经确定的“最后一个订单”将员工加入员工,以便进行相同的比较:

 -- pseudo code...
 SELECT *
 FROM employees e1
 LEFT JOIN employees e2 ON (e1.company_id = e2.company_id AND e1.last_order_date < e2.last_order_date)
 WHERE e2.employee_id IS NULL;

类似的东西,但由于“最后订单日期”是第一个查询的构造,我如何才能在每个员工表上显示这样的结果?

我是否需要使用大量嵌套查询来完成此类操作?临时表是否会成为更好的解决方案?我有什么简单的遗失吗?

我想最终得出每个公司的最后订单:

-- desired output ============================================

company_name  employee_name  order_id  order_date           
Company1      Employee1      1         2016-02-01 00:00:00  
Company2      Employee3      5         2016-01-29 00:00:00  

修改

“&gt;”在第一个查询中应该是“&lt;” - 我已经修改了这个以及输出来显示预期的内容: last 命令而不是第一个。为此道歉;这将最终用于做到这两点,我忘了改变示例的逻辑。

1 个答案:

答案 0 :(得分:1)

修改

如果您想查看提供看似正常但实际上错误的结果的旧答案,请检查编辑历史记录。

如果公司有多个订单且完全相同 order_date,则以下查询将提取多行。 (下面有一个更强大的解决方案)

SELECT
    o.order_date
    , o.order_id
    , e.employee_id
    , c.company_name
    , e.employee_name
FROM (
    SELECT
        MAX(o.order_date) AS order_date, e.company_id
    FROM orders o
    INNER JOIN employees e ON o.employee_id = e.employee_id
    GROUP BY e.company_id
) maxOrderDate
INNER JOIN orders o ON maxOrderDate.order_date = o.order_date
INNER JOIN employees e ON o.employee_id = e.employee_id AND o.employee_id = e.employee_id AND maxOrderDate.company_id = e.company_id
INNER JOIN companies c ON e.company_id = c.company_id

以下查询解释了那些讨厌的多个订单与完全相同的订单状态

SELECT
    o.order_date
    , o.order_id
    , e.employee_id
    , c.company_name
    , e.employee_name
FROM (
    SELECT MAX(o.order_id) AS order_id
    FROM (
        SELECT
            MAX(o.order_date) AS order_date, e.company_id
        FROM orders o
        INNER JOIN employees e ON o.employee_id = e.employee_id
        GROUP BY e.company_id
    ) maxOrderDate
    INNER JOIN orders o ON maxOrderDate.order_date = o.order_date
    INNER JOIN employees e ON o.employee_id = e.employee_id AND o.employee_id = e.employee_id AND maxOrderDate.company_id = e.company_id
    GROUP BY e.company_id
) maxOrder
INNER JOIN orders o ON maxOrder.order_id = o.order_id
INNER JOIN employees e ON o.employee_id = e.employee_id
INNER JOIN companies c ON e.company_id = c.company_id