【问题标题】:Join nearest date (no subquery)加入最近的日期(无子查询)
【发布时间】:2012-01-16 03:55:10
【问题描述】:

我有这个不那么规范化的表:

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)但是子查询对我来说是一个困难的选择,因为我需要的查询结果只是另一个查询的一部分,我担心它会影响性能。

【问题讨论】:

    标签: sql-server-2005 join


    【解决方案1】:

    以下确实使用了子查询(更准确地说,是公共表表达式),但它应该足够高效:

    ;
    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 项可能没有匹配项)。

    参考资料:

    【讨论】:

    • 我认为你是对的,你的子查询只会执行几次,所以没关系。我会测试排名...
    【解决方案2】:

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

    原谅我的英语不好,给我你的意见

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 2015-09-19
      • 2017-10-02
      • 2020-08-26
      • 2018-03-21
      相关资源
      最近更新 更多