как выбрать / найти данные таблицы вместе с последними (многие ко многим) данным - PullRequest
2 голосов
/ 11 января 2012

У меня есть следующие отношения в моих таблицах

item.rb

has_one :item_shipping_detail

item_shipping_detail.rb

belongs_to  :item
has_many :shipping_statuses

status.rb

belongs_to  :item_shipping_detail

Ex. Данные

товар

id   title     city      state             country
 1   Title1    Nagpur    Maharashtra       India

item_shipping_details

id   item_id    price     description
 1   1          10        Electronic

Статусы

id   item_shipping_detail_id    status_city     status_state   status_country  created_at 
 1   1                           Mumbai         Maharashtra    India           2012-01-09 07:58:16
 2   1                           Akola          Maharashtra    India           2012-01-10 07:58:16
 3   1                           Nagpur         Maharashtra    India           2012-01-11 07:58:16

Мне нужен следующий результат (item, item_shipping_details, status (LATEST or MAX)) с использованием одного запроса

id title city     state         country  price  description  status_city  status_state    status_country
 1 Title1 Nagpur  Maharashtra    India    10    Electronic   Nagpur       Maharashtra    India

JYI: - Я использую Rails 2.3.8

Ответы [ 5 ]

4 голосов
/ 20 января 2012

Попробуйте:

select i.id,
       i.title,
       i.city,
       i.state,
       i.country,
       d.price,
       d.description,
       s.status_city,
       s.status_state,
       s.status_country
from items i
left join item_shipping_details d on i.id = d.item_id
left join 
   (select s1.* from statuses s1
     where not exists 
           (select * from statuses s2
        where 
                s2.item_shipping_detail_id = s1.item_shipping_detail_id 
                and s2.created_at> s1.created_at) )  s
on d.id = s.item_shipping_detail_id

Предложение where в подвыборке отфильтровывает любую запись, в которой есть более новая запись. По сути, это то же самое, что и запрос Марка Баннистера (который отлично работает в моей базе данных Postgresql 9.1), но не использует функции секционирования.

Вероятно, это не так эффективно, как функции секционирования, поэтому, если вы можете понять, почему у вас не работает (может быть, настройка совместимости 8.4 или что-то в этом роде?), Я бы пошел с его запросом.

2 голосов
/ 23 января 2012

Использовать DISTINCT ON, расширение postgresql:

select distinct on(i.id, d.id)
   i.id,
   i.title,
   i.city,
   i.state,
   i.country,
   d.price,
   d.description,
   s.status_city,
   s.status_state,
   s.status_country
from items i
left join item_shipping_details d on i.id = d.item_id
left join statuses s on s.item_shipping_detail_id = d.id
order by i.id, d.id, s.created_at desc

Также рассмотрите стандартный синтаксис:

select * from (
select i.id,
   i.title,
   i.city,
   i.state,
   i.country,
   d.price,
   d.description,
   s.status_city,
   s.status_state,
   s.status_country,
   row_number() over(partition by d.id, i.id order by s.created_at desc) as rn
from items i
left join item_shipping_details d on i.id = d.item_id
left join statuses s on s.item_shipping_detail_id = d.id
) tab where tab.rn = 1
0 голосов
/ 23 января 2012

Попробуйте это

select distinct on(i.id, d.id)
   i.id,
   i.title,
   i.city,
   i.state,
   i.country,
   d.price,
   d.description,
   s.status_city,
   s.status_state,
   s.status_country
from items i
left join item_shipping_details d on i.id = d.item_id
left join statuses s on s.item_shipping_detail_id = d.id
where s.id = (select Max(id) as id from statuses where item_shipping_detail_id = d.id)
order by i.id, d.id desc
0 голосов
/ 23 января 2012

Поскольку оказывается, что вы используете 8.3.8 (из комментария к удаленному ответу), вы не можете использовать row_number(). Это означает, что я склонен присоединяться к совокупному подзапросу, чтобы определить, какая запись является самой новой. Что-то вроде ...

SELECT
   i.id,
   i.title,
   i.city,
   i.state,
   i.country,
   d.price,
   d.description,
   s.status_city,
   s.status_state,
   s.status_country
FROM
  items                    i
LEFT JOIN
  item_shipping_details    d
    ON i.id = d.item_id
LEFT JOIN
  (SELECT item_shipping_detail_id, MAX(created_at) AS created_at FROM statuses GROUP BY item_shipping_detail_id)   lookup
    ON lookup.item_shipping_detail_id = d.id
LEFT JOIN
  statuses                 s
    ON  s.item_shipping_detail_id = lookup.item_shipping_detail_id
    AND s.created_at              = lookup.created_at

Примечание: Чтобы оптимизировать подзапрос и объединение, таблицу statuses необходимо правильно проиндексировать; (item_shipping_detail_id, created_at)

Если вместо этого у вас есть индекс (item_shipping_detail_id, id), и вы можете гарантировать, что более высокий id всегда означает, что запись новее, чем запись с более низким id, вы можете заменить в моем запросе вхождения created_at на id.

0 голосов
/ 11 января 2012

Вы должны включить «что-то» в статусы, чтобы вы могли выбрать последний статус, например, дату прибытия или около того.Невозможно ответить на ваш вопрос, пока вы не предоставите этот дополнительный столбец.

...