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

时间:2016-01-06 21:51:28

标签: mysql sql group-by subquery

我有一个表,我需要查询以获取描述中包含特定数据的最新记录。表格列包含(部分)以下内容:

+-----------+------------+-------------------+
| 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查询没有太多经验,但是这更适合于子查询的左连接或内连接吗?

我想过首先使用子查询来提取描述中包含所需文本的所有记录,然后查询该子查询以选择/分组最近,但不知道是否有必要。< / p>

非常感谢您的帮助!

更新 此服务器主机正在运行旧版本的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);

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

4 个答案:

答案 0 :(得分:1)

在您的查询中,没有什么能保证mysql会选择那些具有max(date)值的描述字段。实际上,你的版本是违反mysql标准的,只能在某些配置设置下使用mysql。

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

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%";

<强>更新

Mysql v4.0不支持子查询,因此上述方法不适用。您仍然可以使用左连接方法,在该方法中您自己加入products表并使用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;

答案 1 :(得分:1)

也许你的旧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
    )

许多平台可以处理这种子查询,然后才能使用&#34;派生表&#34;和&#34;内联视图&#34;在from子句中。我不确定MySQL。

答案 2 :(得分:0)

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

这是因为你依赖MySQL中的一个叫做GROUP BY的“扩展”的功能。此“功能”允许您仅在group by子句中包含AccountID;但未提及列Description。所以MySQL在MuSQL文档中选择“任何值”:

  

MySQL扩展了GROUP BY的标准SQL使用,以便选择列表可以引用GROUP BY子句中未命名的非聚合列。这意味着前面的查询在MySQL中是合法的。您可以通过避免不必要的列排序和分组来使用此功能来获得更好的性能。但是,当GROUP BY中未命名的每个非聚合列中的所有值对于每个组都相同时,这非常有用。 T 服务器可以自由选择每个组中的任何值,因此除非它们相同,否则所选择的值是不确定的。此外,添加ORDER BY子句不会影响每个组中值的选择。   请参阅:12.16.3 MySQL Handling of GROUP BY大胆强调添加

简要说明您在“描述”列中获得的结果,您无法通过当前查询进行控制。

如果您的版本支持子查询,这些将有助于:

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 |

答案 3 :(得分:0)

在最近的日期之前的第一组,然后按日期和accountId将其与产品表连接。如果您想要更多产品表的陈述,那么外面的地方就会出现。

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