MySQL使用查找表中的值从多个表中按日期获取最新记录

时间:2010-10-07 09:08:34

标签: mysql datetime max coalesce

我想获取给定MainNumber的最新MainNumber,Serial,BarType和Notes(如果存在)。请注意,BarType存储在查找表中,并使用BarID进行引用。

Unreason得出了这个:

SELECT @MainNumber, COALESCE(n.Notes, 'None')
FROM numbers 
     LEFT JOIN notes n ON numbers.MainNumber = n.MainNumber
     LEFT JOIN notes n2 ON n.MainNumber = n2.MainNumber AND n.Date < n2.Date
WHERE n2.Date IS NULL AND numbers.MainNumber = @MainNumber

无论Notes是NULL还是CREATE TABLE `numbers` ( `ID` int(10) unsigned NOT NULL auto_increment, `MainNumber` varchar(11) NOT NULL, `Serial` varchar(20) NOT NULL, `Date` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, PRIMARY KEY (`ID`), UNIQUE KEY `Serial` (`Serial`) ) ENGINE=MyISAM AUTO_INCREMENT=460 DEFAULT CHARSET=latin1 ,这都很好,但现在我需要Serial和BarType。 MainNumber可能在其生命周期内被分配给多个Serial,但我只想要最新的Serial。 (我需要在其他表中的大约15个其他字段中执行此操作,因此在可能的情况下将会感谢高效的答案)

表格

数字表:

CREATE TABLE `notes` (
  `ID` int(10) unsigned NOT NULL auto_increment,
  `MainNumber` varchar(11) NOT NULL,
  `Notes` longtext NOT NULL,
  `Date` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  PRIMARY KEY  (`ID`),
  KEY `MainNumber` (`MainNumber`)
) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC

备注表:

CREATE TABLE `ref_bars` (
  `BarID` varchar(6) NOT NULL,
  `BarType` varchar(30) NOT NULL,
  PRIMARY KEY  USING BTREE (`BarID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

ref_bars表:

CREATE TABLE `bars` (
  `ID` int(10) unsigned NOT NULL auto_increment,
  `MainNumber` varchar(11) NOT NULL,
  `BarID` varchar(6) NOT NULL,
  `Date` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  PRIMARY KEY  (`ID`),
  KEY `MainNumber` (`MainNumber`)
) ENGINE=MyISAM AUTO_INCREMENT=212 DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC
吧表:

SELECT * FROM notes

示例数据

'ID','MainNumber','Notes','Date' '1','1','repaired','2009-03-23 12:00:00' '2','1','replaced','2009-08-15 19:20:05'

SELECT * FROM numbers

注意:MainNumber的两行= 1,但MainNumber为2的行没有。这些ID只是技术性的,从不使用。

'ID','MainNumber','Serial','Date' '1','1','4642785154854','2008-08-15 12:30:00' '2','1','4642315642316','2009-08-15 12:50:00' '3','2','5412558456223','2010-08-15 11:30:00'

SELECT * FROM bars

'ID','MainNumber','BarID','Date' '1','1',1,'2008-08-15 12:30:00' '2','1',2,'2009-08-15 12:50:00' '3','2',2,'2010-08-15 11:30:00'

SELECT * FROM ref_bars

'BarID','BarType' '1','Banned' '2','Payment required'

MainNumber,Notes,Banned,Unpaid
'1','replaced','Yes','Yes'

预期输出

MainNumber = 1

MainNumber,Notes,Banned,Unpaid
'2','None','No','Yes'

MainNumber = 2

{{1}}

编辑:修正并测试它,同时让事情更清晰(希望如此)。我今天早些时候被赶出去做其他的事情,很抱歉用一个写得不好的,不完整的问题浪费了人们的时间。

更新以澄清更复杂的要求

5 个答案:

答案 0 :(得分:1)

选择忽略您的初始查询,因为它们不能重现您的问题我正在查看您预期的输出我假设您正在尝试实现以下目标:

  

鉴于MainNumber寻找记录   在表格notes中并返回该行   最大日期,如果没有记录   在给出的表格中   MainNumber然后返回常量   'None'

(这可能不是所要求的,所以如果不是,请更正预期的输出)

这可以通过例如

轻松实现
SELECT @MainNumber, n.Notes
FROM notes n
     LEFT JOIN notes n2 ON n.MainNumber = n2.MainNumber AND n.Date < n2.Date
WHERE n2.Date IS NULL AND n.MainNumber = @MainNumber

这将返回笔记中的最新一行。 现在从应用程序端,如果它没有返回任何行,只需打印@MainNumber,'None',那就是它......

如果您寻找纯SQL(并假设您确实需要数字表中的其他一些列),那么您可以这样做:

SELECT @MainNumber, COALESCE(n.Notes, 'None')
FROM numbers 
     LEFT JOIN notes n ON numbers.MainNumber = n.MainNumber
     LEFT JOIN notes n2 ON n.MainNumber = n2.MainNumber AND n.Date < n2.Date
WHERE n2.Date IS NULL AND numbers.MainNumber = @MainNumber

编辑: 第一个查询已经过测试

mysql> SET @MainNumber = 1;                                                     Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @MainNumber, n.Notes FROM notes n      LEFT JOIN notes n2 ON n.MainNumber = n2.MainNumber AND n.Date < n2.Date WHERE n2.Date IS NULL AND n.MainNumber = @MainNumber;
+-------------+----------+
| @MainNumber | Notes    |
+-------------+----------+
|           1 | replaced | 
+-------------+----------+
1 row in set (0.00 sec)

如果数字表中有多个条目,第二个查询最初返回多行,DISTINCT修复了

mysql> SET @MainNumber = 1;                                                     Query OK, 0 rows affected (0.00 sec)

mysql> SELECT DISTINCT @MainNumber, COALESCE(n.Notes, 'None')
    -> FROM numbers 
    ->      LEFT JOIN notes n ON numbers.MainNumber = n.MainNumber
    ->      LEFT JOIN notes n2 ON n.MainNumber = n2.MainNumber AND n.Date < n2.Date
    -> WHERE n2.Date IS NULL AND numbers.MainNumber = @MainNumber
    -> ;
+-------------+---------------------------+
| @MainNumber | COALESCE(n.Notes, 'None') |
+-------------+---------------------------+
|           1 | replaced                  | 
+-------------+---------------------------+
1 row in set (0.00 sec)

mysql> SET @MainNumber = 2;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT DISTINCT @MainNumber, COALESCE(n.Notes, 'None')
    -> FROM numbers 
    ->      LEFT JOIN notes n ON numbers.MainNumber = n.MainNumber
    ->      LEFT JOIN notes n2 ON n.MainNumber = n2.MainNumber AND n.Date < n2.Date
    -> WHERE n2.Date IS NULL AND numbers.MainNumber = @MainNumber
    -> ;
+-------------+---------------------------+
| @MainNumber | COALESCE(n.Notes, 'None') |
+-------------+---------------------------+
|           2 | None                      | 
+-------------+---------------------------+
1 row in set (0.00 sec)

答案 1 :(得分:1)

你可以按照Unreason的建议加入JOIN。

另一种方法是使用子查询:

select distinct s.MainNumber, 
COALESCE(
  (select n.notes from notes n where n.MainNumber=s.MainNumber order by n.Date desc limit 1), 
  'None') as LastNote
from numbers s
WHERE s.MainNumber=?

请注意,使用JOIN的解决方案可能会或可能不会更好,您必须尝试。

另请注意,“LIMIT”是特定于MySQL的(不是ANSI SQL),因此如果您打算迁移到另一个DBMS,请注意。

答案 2 :(得分:0)

也许是这样的?

LEFT JOIN (select notes n
JOIN (
    SELECT
        MAX(Date) AS Date,
        MainNumber
    FROM notes
    GROUP BY MainNumber
) AS nx
    ON n.MainNumber = nx.MainNumber AND n.Date = nx.Date) n
ON notes.MainNumber = main.MainNumber

我删除了where子句,因为你没有描述你拥有它的原因。如果您需要它,您可以再次插入它。

编辑:查询已更新,但仍然很难理解您想要的内容。当我编写示例数据时,我认为每个表有2-3行以及预期输出,如果你可以简化更好的示例。希望它有所帮助。

答案 3 :(得分:0)

SELECT MainNumber,
       COALESCE(Notes,"None") as Notes,
       if(BarID=1,"Yes","No") as Banned,
       if(BarID=2,"Yes","No") as Unpaid,
       COALESCE(notes.Date,CURRENT_TIMESTAMP) as Date
FROM numbers LEFT JOIN notes USING (MainNumber)
     LEFT JOIN bars USING (MainNumber)
GROUP BY MainNumber,Date
HAVING Date=COALESCE(MAX(notes.Date),CURRENT_TIMESTAMP)

答案 4 :(得分:0)

我终于解决了。它实际上比我想象的要简单得多。它也更接近我写的原始查询。

SELECT
    s.MainNumber,
    n.Notes
FROM numbers s
JOIN (
    SELECT
        MAX(Date) AS Date,
        MainNumber
    FROM numbers
    WHERE MainNumber = 2
) AS sx
    ON s.MainNumber = sx.MainNumber AND s.Date = sx.Date

LEFT JOIN notes n
    ON s.MainNumber = n.MainNumber
LEFT JOIN (
    SELECT
            MAX(Date) AS Date,
            COALESCE(MainNumber,0) AS MainNumber
    FROM notes
    WHERE MainNumber = 2
) AS nx
    ON n.MainNumber = nx.MainNumber AND n.Date = nx.Date