MySQL Получить последнюю запись по дате из нескольких таблиц со значениями из справочных таблиц

0

Я хочу получить последние MainNumber, Serial, BarType и Notes для данного MainNumber, если они существуют. Обратите внимание, что BarType хранится в справочной таблице и ссылается на BarID.

Неисправность придумала следующее:

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 или нет, но теперь мне нужен Serial и BarType. Возможно, в течение всего срока его службы может быть назначено имя MainNumber, но мне нужен только последний Serial. (Мне нужно будет сделать это примерно с 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'

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

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

  • 3
    Не могли бы вы предоставить пример данных? (с ожидаемым результатом)
  • 0
    Как пишет mastoj, вы получите гораздо лучшие ответы, если разместите воспроизводимый контрольный пример: все определения таблиц (или, по крайней мере, достаточно для выполнения SQL), плюс весь используемый вами запрос, а также некоторые тестовые данные плюс ожидаемый результат.
Показать ещё 10 комментариев
Теги:
datetime
coalesce
max

5 ответов

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
1

Вы можете сделать это с помощью 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
    @ Алан, этот ответ тоже работает (возвращает запрошенные данные). Я проверил это.
1

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

С учетом MainNumber найдите запись в tabel 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

EDIT: Первый запрос проверен

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)
  • 0
    Спасибо за настойчивость со мной. Вы поняли, что я хотел, но я не уверен, как использовать ваши запросы. Я посмотрел документацию по переменным в MySQL, а затем попытался SET @MainNumber = 1; запрос SET @MainNumber = 1; , но тогда я не получаю результат. Я пробовал также с 2 , '1' и '2' . Также мне нужно получить MainNumber из numbers как при notes MainNumber = 2 в notes не будет строк. Я снова обновлю вопрос.
  • 0
    @ Алан, обновит ответ, все равно будет ошибка во втором
Показать ещё 8 комментариев
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)
  • 0
    Это не работает Я попытался исправить это (изменив BarTypes, bar_ref на ref_bars, добавив LEFT JOIN к барам и добавив notes.Date к предложению SELECT ), но затем он не дал результатов. Не могли бы вы немного поработать над этим?
  • 0
    возился и дразнил
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-clause, так как вы не описали, почему у вас это есть. Если вам это нужно, вы можете просто вставить его снова.

EDIT: Запрос обновляется, но по-прежнему сложно понять, что вы хотите. Когда я писал данные примера, я имел в виду 2-3 строки для каждой таблицы вместе с ожидаемым выходом, и если бы вы могли упростить пример, который был бы еще лучше. Надеюсь, что это поможет.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню