Присоединиться к ближайшей дате (без подзапроса) - PullRequest
2 голосов
/ 16 января 2012

У меня есть не очень нормализованная таблица:

ItemName    Type    DateTransferred

Hand Drill  IN  2012-01-16 11:06:10.077
Hand Drill  OUT 2012-01-16 11:06:16.563
Hand Drill  IN  2012-01-16 11:06:26.780
Grinder     IN  2012-01-16 11:06:33.917
Hand Drill  OUT 2012-01-16 11:06:45.443

Создать запрос:

CREATE TABLE [dbo].[TransferLog](
    [ItemName] [nvarchar](50) NOT NULL,
    [Type] [nvarchar](3) NOT NULL,
    [DateTransferred] [datetime] NOT NULL
) ON [PRIMARY]


ALTER TABLE [dbo].[TransferLog] 
ADD  CONSTRAINT [DF_TransferLog_DateTransferred]  
    DEFAULT (getdate()) FOR [DateTransferred]

Как правило, в приведенной выше таблице регистрируются заимствованные элементы (тип: IN) и элементы, возвращенные (тип: OUT) командой проекта со склада.

Чего я хочу добиться, так это получить все заимствованное оборудование, когда оно было заимствовано (IN) и когда оно было возвращено (OUT). Проблема возникает при попытке сопоставить «транзакцию заимствования» с соответствующей «транзакцией возврата», поскольку единственное отношение, которое они имеют, - это ItemName:

Выбор всех «транзакций займа»:

select tIn.ItemName, tIn.DateTransferred as DateBorrowed
from transferLog as tIn
where tIn.[type] = 'IN'

Результат:

ItemName    DateBorrowed

Hand Drill  2012-01-16 11:06:10.077
Hand Drill  2012-01-16 11:06:26.780
Grinder     2012-01-16 11:06:33.917

Попытка выбрать все «заимствованные транзакции» и соответствующую им «возвратную транзакцию»:

select tIn.ItemName, tIn.DateTransferred as DateBorrowed, 
    tOut.DateTransferred as   DateReturned
from transferLog as tIn
left join transferLog as tOut
on tIn.ItemName = tOut.ItemName
    and tOut.[type] = 'OUT'
where tIn.[type] = 'IN'

Результат:

ItemName    DateBorrowed                    DateReturned

Hand Drill  2012-01-16 11:06:10.077     2012-01-16 11:06:16.563
Hand Drill  2012-01-16 11:06:10.077     2012-01-16 11:06:45.443
Hand Drill  2012-01-16 11:06:26.780     2012-01-16 11:06:16.563
Hand Drill  2012-01-16 11:06:26.780     2012-01-16 11:06:45.443
Grinder     2012-01-16 11:06:33.917     NULL

Обратите внимание, что каждая "заимствованная транзакция" должна иметь только одну или не иметь соответствующей "транзакции возврата", приведенный выше результат сопоставляет каждую "заимствованную транзакцию" с каждой "транзакцией возврата", если они имеют одинаковые ItemName. Результат должен быть:

ItemName    DateBorrowed                    DateReturned

Hand Drill  2012-01-16 11:06:10.077     2012-01-16 11:06:16.563
Hand Drill  2012-01-16 11:06:26.780     2012-01-16 11:06:45.443
Grinder     2012-01-16 11:06:33.917     NULL

Теперь я подумываю о том, как сопоставить «транзакцию возврата» с DateTransferred, большим и ближайшим к «транзакции заимствования» DateTransferred. Что-то вроде:

select tIn.ItemName, tIn.DateTransferred as DateBorrowed, 
    tOut.DateTransferred as DateReturned
from transferLog as tIn
left join transferLog as tOut
on tIn.ItemName = tOut.ItemName
and tOut.[type] = 'OUT'
and 
        tOut.DateTransferred > tIn.DateTransferred 
        -- AND NEAREST tOut.DateTransferred TO tIn.DateTransferred
where tIn.[type] = 'IN'

Я читаю это ( SQL Join на Ближайший меньше, чем дата ) и это ( Соединение таблиц на ближайшую дату в прошлом, в MySQL ), но подзапрос - трудный выбор для меня поскольку результат запроса, который мне нужен, является лишь частью другого запроса, боюсь, это повлияет на производительность.

Ответы [ 2 ]

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

В следующем примере используется подзапрос (точнее, обычное табличное выражение), но он должен быть достаточно эффективным:

;
WITH ranked AS (
  SELECT
    *,
    rnk = ROW_NUMBER() OVER (PARTITION BY ItemName, Type ORDER BY DateTransferred)
  FROM TransferLog
)
SELECT
  r_in.ItemName,
  r_in.DateTransferred AS DateBorrowed,
  r_out.DateTransferred AS DateReturned
FROM ranked r_in
  LEFT JOIN ranked r_out ON r_out.Type = 'OUT'
    AND r_in.ItemName = r_out.ItemName
    AND r_in.rnk = r_out.rnk
WHERE r_in.Type = 'IN'

Как видите, идея состоит в том, чтобы ранжировать строки IN и OUT по отдельности, а затем сопоставить первые со вторыми (используя внешнее объединение, поскольку последний элемент IN может не совпадать).

Ссылки:

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

кажется поздно ответить на этот вопрос, но я думаю, что должен ответить на него, так как я видел это. что-то не так в ответе если в таблицу добавлена ​​новая запись, как указано ниже: Ручная дрель 2012-01-16 11: 06: 10.077 2010-01-16 00: 00: 00.563 это приведет к неправильным последствиям. Это мой ответ.

select 
tIn.ItemName,
tIn.DateTransferred as DateBorrowed,
tOut.DateTransferred as DateReturned
from TransferLog as tIn
left join TransferLog as tOut
on tIn.ItemName=tOut.ItemName 
and tOut.[Type]='out' 
and tOut.DateTransferred=
(
    select 
    top 1 DateTransferred 
    from TransferLog as temp
    where temp.DateTransferred>tIn.DateTransferred 
    and temp.ItemName=tIn.ItemName 
    and tIn.[Type]='in' 
    and temp.[Type]='out'
    order by temp.DateTransferred asc
)
where tIn.[Type]='in'

Простить мой плохой английский и высказать свое мнение

...