MySQL Получить последнюю запись по дате из нескольких таблиц со значениями из справочных таблиц - PullRequest
0 голосов
/ 07 октября 2010

Я хочу получить последние MainNumber, Serial, BarType и Notes для данного MainNumber, если они существуют.Обратите внимание, что 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 или нет, но теперь янужен серийный номер и тип BarType.Возможно, MainNumber был назначен нескольким Сериалам в течение срока его службы, но я хочу только последний Серийный номер.(Мне нужно будет сделать это примерно с 15 другими полями в других таблицах, поэтому, если это возможно, мы будем благодарны за эффективный ответ)

Таблицы

Таблица чисел:

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

Таблица примечаний:

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

Таблица ref_bars:

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

Таблица баров:

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'

Примечание: две строки для MainNumber = 1, но нет строки для MainNumber из 2. Идентификаторы являются только техническими и никогда не используются.

SELECT * FROM numbers:

'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 = 1

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

MainNumber = 2

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

Редактировать: Исправлено и проверено, при этом все проясняется (надеюсь).Сегодня я был вынужден заняться другими делами, извиняюсь за трату времени людей на плохо написанный, неполный вопрос.

Обновлен для уточнения более сложных требований

Ответы [ 5 ]

1 голос
/ 11 октября 2010

Выбор игнорировать ваши начальные запросы, поскольку они не воспроизводят вашу проблему. Я смотрю на ожидаемый вами результат. Я предполагаю, что вы пытаетесь добиться следующего:

Учитывая 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 голос
/ 11 октября 2010

Вы можете сделать это с помощью JOIN, как предлагает Unreason.

Другой способ будет с подзапросом:

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), поэтому будьте осторожны, если вы собираетесь перейти на другую СУБД.

0 голосов
/ 14 октября 2010

Я наконец-то решил это.На самом деле все намного проще, чем я думал.Это также ближе к исходному запросу, который я писал.

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
0 голосов
/ 14 октября 2010
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)
0 голосов
/ 11 октября 2010

Может как то так?

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 строки для каждой таблицы вместе с ожидаемым результатом, и если бы вы могли упростить пример, это было бы еще лучше. Надеюсь, это поможет.

...