MySQL 查询:Select 列包含特定条件的最新记录

MySQL Query: Select latest record where column contains certain criteria

我有一个 table 需要查询以获得描述包含某些数据的最新记录。 table 列包含(部分)以下内容:

+-----------+------------+-------------------+
| AccountID |    Date    |    Description    |
+-----------+------------+-------------------+
|    125060 | 2006-02-11 | Red Apple         |
|    125060 | 2007-03-23 | Yellow Banana     |
|    125060 | 2009-04-03 | Yellow Apple      |
|    125687 | 2006-03-10 | Red Apple         |
|    139554 | 2007-06-29 | Orange Orange     |
|    139554 | 2009-07-24 | Green Apple       |
|    145227 | 2008-11-22 | Green Pear        |
|    145227 | 2012-04-16 | Yellow Grapefruit |
|    154679 | 2014-05-22 | Purple Grapes     |
|    163751 | 2012-02-11 | Green Apple       |
|    ...    |    ...     |       ...         |
+-----------+------------+-------------------+

(还有几个栏目,几十万条记录,目前我只关心这些)

对于此示例,我想要包含 "Apple." 的 AccountID 子集的最新记录,我要查找的结果是:

+-----------+------------+--------------+
| AccountID |    Date    | Description  |
+-----------+------------+--------------+
|    125060 | 2009-04-03 | Yellow Apple |
|    125687 | 2006-03-10 | Red Apple    |
|    139554 | 2009-07-24 | Green Apple  |
+-----------+------------+--------------+

我当前使用的查询是:

SELECT AccountID, max(Date), Description 
FROM products
WHERE Description like "%Apple%" and (AccountID=125060 or AccountID=125687 or AccountID=139554)
GROUP BY AccountID;

不幸的是,结果产生了以下曲调:

+-----------+------------+-------------------+
| AccountID |    Date    |    Description    |
+-----------+------------+-------------------+
|    125060 | 2009-04-03 | Red Apple         |
|    125687 | 2006-03-10 | Red Apple         |
|    139554 | 2009-07-24 | Green Apple       |
+-----------+------------+-------------------+

其中 AccountID 正确分组,并且选择了适当的(最近的)日期,但描述仍然返回与 WHERE/like 子句匹配的第一个描述......而不是描述与所选日期的记录相关联。

我以前从未见过这样的事情。这是我做错了什么吗?我对高级 MySQL 查询没有太多经验,但这是否更适合子查询的左连接或内连接?

我考虑过首先使用子查询来提取描述中包含所需文本的所有记录,然后将该子查询查询到最近的 select/group,但不知道是否有必要这样做与否。

非常感谢您的帮助!

更新 此服务器主机 运行 是 mySQL (4.0.17) 的旧版本。显然这个版本太旧,不支持子查询。多亏了 Shadow 和 shawnt00,左连接似乎也可以实现同样的效果。这是我当前使用的查询:

SELECT p1.*
FROM products p1
LEFT JOIN products p2
on p1.AccountID=p2.AccountID and p1.Date<p2.Date and p2.Description like "%Apple%"
where p1.Description like "%Apple%" and p2.Date is null and (p1.AccountID=125060 or p1.AccountID=142580 or p1.AccountID=145135 or p1.AccountID=139254);

如果此查询有任何问题,我会post 回复。谢谢大家!

在您的查询中,没有任何东西可以保证 mysql 将 select 那些具有最大(日期)值的描述字段。实际上,您的版本违反了 mysql 标准,只能在某些配置设置下在 mysql 中工作。

解决方案是通过帐户 ID 获取最大日期,其中描述与子查询中的条件匹配,并使用帐户 ID 和最大日期将其连接回 table 本身:

SELECT p.AccountID, p.Date, p.Description
FROM products p
INNER JOIN (SELECT AccountID, max(Date) as maxdate
FROM products
WHERE Description like "%Apple%" and (AccountID=125060 or AccountID=125687 or AccountID=139554)
GROUP BY AccountID) t
ON p.AccountID=t.AccountID and p.Date=t.maxdate
WHERE Description like "%Apple%";

更新

Mysqlv4.0不支持子查询,所以上面的方法不适用。您仍然可以使用左连接方法,您自己连接产品 table 并使用 is null 表达式来查找较大日期不属于的那些日期:

select p1.*
from products p1
left join products p2
on p1.accountid=p2.accountid and p1.date<p2.date
where Description like "%Apple%" and p2.date is null;

"返回与 WHERE/like 子句匹配的第一个描述...而不是与选定日期的记录相关的描述"

那是因为您依赖 MySQL 中称为 "extension" 的功能来 GROUP BY。 "feature" 允许您在 group by 子句中只包含 AccountID;但是没有提到 Description 列。所以MySQL在MuSQL文档中选择“任意值”:

MySQL extends the standard SQL use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause. This means that the preceding query is legal in MySQL. You can use this feature to get better performance by avoiding unnecessary column sorting and grouping. However, this is useful primarily when all values in each nonaggregated column not named in the GROUP BY are the same for each group. The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause. see: 12.16.3 MySQL Handling of GROUP BY bold emphasis added

简而言之,您无法通过当前查询控制您在描述列中获得的结果。

如果您的版本支持 sub-queries 这些将有助于:

SELECT
      p.*
FROM products p
      INNER JOIN (

            SELECT
                  AccountID
                , MAX(`date`) AS dt
            FROM products
            WHERE Description LIKE '%Apple%'
                  AND (AccountID = 125060
                  OR AccountID = 125687
                  OR AccountID = 139554)
            GROUP BY
                  AccountID
      ) m ON p.AccountID = m.AccountID
                  AND p.`date` = m.dt
/* and if required */
WHERE p.descrption LIKE '%Apple%'
;

到目前为止不支持 ROW_NUMBER() 的 MySQL 的替代方法是模仿该函数,如下所示:SQL Fiddle

MySQL 5.6 架构设置:

CREATE TABLE Products
    (`AccountID` int, `Date` datetime, `Description` varchar(17))
;

INSERT INTO Products
    (`AccountID`, `Date`, `Description`)
VALUES
    (125060, '2006-02-11 00:00:00', 'Red Apple'),
    (125060, '2007-03-23 00:00:00', 'Yellow Banana'),
    (125060, '2009-04-03 00:00:00', 'Yellow Apple'),
    (125687, '2006-03-10 00:00:00', 'Red Apple'),
    (139554, '2007-06-29 00:00:00', 'Orange Orange'),
    (139554, '2009-07-24 00:00:00', 'Green Apple'),
    (145227, '2008-11-22 00:00:00', 'Green Pear'),
    (145227, '2012-04-16 00:00:00', 'Yellow Grapefruit'),
    (154679, '2014-05-22 00:00:00', 'Purple Grapes'),
    (163751, '2012-02-11 00:00:00', 'Green Apple')
;

查询 1:

SELECT
      p.AccountID, p.Date, p.Description
FROM (
      SELECT
             @row_number:= case when @acct = pr.AccountID then @row_number + 1 else 1 end as rownumber
           , @acct := pr.AccountID as acct
           , pr.AccountID, pr.Date, pr.Description
      FROM products pr
      CROSS JOIN (select @row_number := 0, @acct := '') as rn
      WHERE Description LIKE '%Apple%'
             AND (AccountID = 125060
             OR AccountID = 125687
             OR AccountID = 139554)
      ORDER BY pr.AccountID, pr.Date DESC
      ) p
WHERE p.rownumber = 1
ORDER BY p.AccountID

Results:

| AccountID |                    Date |  Description |
|-----------|-------------------------|--------------|
|    125060 | April, 03 2009 00:00:00 | Yellow Apple |
|    125687 | March, 10 2006 00:00:00 |    Red Apple |
|    139554 |  July, 24 2009 00:00:00 |  Green Apple |

首先按最近日期分组,然后按日期和帐户 ID 将其与您的产品 table 合并。如果您想要更多关于产品的声明,请到外面去 table。

SELECT 
    P.*
FROM products P INNER JOIN (
    SELECT 
        AccountID,
        MAX(Date) MostRecentDate    
    FROM products       
    WHERE Description LIKE '%Apple%' AND P.AccountID IN (125060 , 125687, 139554)
    GROUP BY AccountID
) MR ON MR.AccountID = P.AccountID AND MR.MostRecentDate = P.Date

也许你的老 MySQL 可以处理这个版本。它将 AccountIDDate 值组合成一个与 in.

一起使用的结果
select
    p.Account, p.Date, p.Description
from
    products p
where
        p.AccountID in (125060, 125687, 139554)
    and p.Description like '%Apples%'
    and concat(cast(p.AccountID as varchar(8)), date_format(p.Date, '%Y%m%d')) in
    (
        select concat(cast(p2.AccountID as varchar(8)), date_format(max(p2.Date), '%Y%m%d'))
        from products p2
        where p2.Description like '%Apple%'
        group by p2.AccountID
    )

许多平台在使用 from 子句中的 "derived tables" 和 "inline views" 之前可以处理这种子查询。不过,我不确定 MySQL。