加入最近的日期(无子查询)

时间:2012-01-16 03:55:11

标签: sql-server-2005 join

我有这个不那么正常化的表:

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 on Nearest less than date)和这个(Join tables on nearest date in the past, in MySQL)但是子查询对我来说是一个困难的因为我需要的查询结果只是另一个查询的一部分,我害怕它会影响表现。

2 个答案:

答案 0 :(得分:4)

以下使用子查询(更准确地说,是一个常见的表表达式),但它应该足够有效:

;
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'

如您所见,我们的想法是分别对INOUT行进行排名,然后将前者与后者匹配(使用外部联接,因为最后IN项可以没有比赛)。

参考文献:

答案 1 :(得分:0)

回答这个问题似乎很晚,但我想我应该回答它,因为我已经看到了。 答案中出现了问题。 如果插入表中有以下新记录: 手钻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'

原谅我可怜的英语并给我你的意见