【问题标题】:Reuse last value in row and make first value on next - SQL server重用行中的最后一个值并在下一个上创建第一个值 - SQL Server
【发布时间】:2017-11-03 11:34:48
【问题描述】:

所以,我有这个 SQL 表:

Traveller      checkin       dateTime
5566           Madrid        2017-01-01 01:00:00.00
5566           Barcelona     2017-01-02 03:00:00.00
5566           Berlin        2017-01-03 02:00:00.00
5566           Paris         2017-01-06 05:00:00.00
5566           London        2017-01-07 06:00:00.00
5566           Madrid        2017-01-08 02:00:00.00
4422           Moscow        2017-01-03 08:00:00.00
4422           Madrid        2017-01-04 07:00:00.00
4422           Barcelona     2017-01-05 03:00:00.00
8833           Barcelona     2017-02-01 08:00:00.00
8833           Berlin        2017-02-02 04:00:00.00
8833           London        2017-02-03 01:00:00.00
8833           Berlin        2017-02-03 22:00:00.00
9966           Paris         2017-02-03 04:00:00.00
9966           London        2017-02-04 06:00:00.00
9966           Berlin        2017-02-05 01:00:00.00
...            ...           ...

是否有可能以某种方式将这些排序到一个从-到表中,使用除第一个签入之外的所有内容作为起点和终点。像这样:

Traveller      From       To
5566           Madrid     Barcelona
5566           Barcelona  Berlin
5566           Berlin     Paris
5566           Paris      London
5566           London     Madrid
4422           Moscow     Madrid
4422           Madrid     Barcelona
8833           Barcelona  Berlin
8833           Berlin     London
8833           London     Berlin
...            ...        ...

我知道 SQL 的基础知识,但我仍在学习,所以如果有人能想到可以做到(或不这样做)的方法,请帮助我了解它是如何工作的。

非常感谢!

【问题讨论】:

  • 您使用的 SQL Server 版本是什么?
  • 表格没有内在的顺序。我希望有一些额外的列可以用来定义订单?
  • 我使用的是 SQL Server 2008
  • 是的,每次签到都有时间戳
  • 抱歉,再次检查,它的 SQL server 2012 正在运行

标签: sql-server sql-server-2012


【解决方案1】:

您可以使用LEAD function 返回下一行的值,例如

select 
    Traveller,
    checkin,
    LEAD(checkin,1) OVER(PARTITION by traveller order by timestamp) as destination
from @t t

这将为最后一段返回一个 NULL 目的地。要删除它,您可以使用 CTE 过滤数据:

with checkins as (
    select 
        Traveller,
        checkin,LEAD(checkin,1) OVER(PARTITION by traveller order by timestamp) as destination
    from @t t)
select * from checkins 
where destination is not null

测试所有这些:

declare @t table (Traveller int not null, checkin varchar(19) not null, TimeStamp datetime2 not null);

insert into @t (Traveller,checkin,TimeStamp) values
(5566,'Madrid',   '2017-06-02T07:56:01'),
(5566,'Barcelona','2017-06-02T07:56:02'),
(5566,'Berlin',   '2017-06-02T07:56:03'),
(5566,'Paris',    '2017-06-02T07:56:04'),
(5566,'London',   '2017-06-02T07:56:05'),
(5566,'Madrid',   '2017-06-02T07:56:06'),
(4422,'Moscow',   '2017-06-02T07:56:07'),
(4422,'Madrid',   '2017-06-02T07:56:08'),
(4422,'Barcelona','2017-06-02T07:56:09'),
(8833,'Barcelona','2017-06-02T07:56:10'),
(8833,'Berlin',   '2017-06-02T07:56:11'),
(8833,'London',   '2017-06-02T07:56:12'),
(8833,'Berlin',   '2017-06-02T07:56:13'),
(9966,'Paris',    '2017-06-02T07:56:14'),
(9966,'London',   '2017-06-02T07:56:15'),
(9966,'Berlin',   '2017-06-02T07:56:16');



with checkins as (
    select 
        Traveller,
        checkin,LEAD(checkin,1) OVER(PARTITION by traveller order by timestamp) as destination
    from @t t)
select * from checkins 
where destination is not null

将返回:

Traveller   checkin             destination
----------- ------------------- -------------------
4422        Moscow              Madrid
4422        Madrid              Barcelona
5566        Madrid              Barcelona
5566        Barcelona           Berlin
5566        Berlin              Paris
5566        Paris               London
5566        London              Madrid
8833        Barcelona           Berlin
8833        Berlin              London
8833        London              Berlin
9966        Paris               London
9966        London              Berlin

【讨论】:

  • 虽然我们可能会鼓励人们迁移到受支持的版本,但可悲的现实是人们确实会继续使用旧版本。因此,由于 OP 已专门标记为 2008,这不会解决他们的问题。
  • @Damien_The_Unbeliever Technet 文档不会显示不受支持版本的结果,除非您在 URL 中明确键入版本号。找到在这些版本中有效的方法并不容易
  • 嘿,很抱歉给您带来不便,但它正在运行 SQL Server 2012
  • @Katalo 如果您检查不同的 SQL 查询,您会明白为什么会有显着差异。也会影响性能
  • 这符合我的要求!谢谢!
【解决方案2】:

您可以使用row_number 窗口函数。这是处理此问题的 Sql Server 2008 方式。这里是:

;with raw_data (id, city, arrival) as (
    select 5566, 'Madrid', '2017-01-01 01:00:00.00' union all
    select 5566, 'Barcelona', '2017-01-02 03:00:00.00' union all
    select 5566, 'Berlin', '2017-01-03 02:00:00.00' union all
    select 5566, 'Paris', '2017-01-06 05:00:00.00' union all
    select 5566, 'London', '2017-01-07 06:00:00.00' union all
    select 5566, 'Madrid', '2017-01-08 02:00:00.00' union all
    select 4422, 'Moscow', '2017-01-03 08:00:00.00' union all
    select 4422, 'Madrid', '2017-01-04 07:00:00.00' union all
    select 4422, 'Barcelona', '2017-01-05 03:00:00.00' union all
    select 8833, 'Barcelona', '2017-02-01 08:00:00.00' union all
    select 8833, 'Berlin', '2017-02-02 04:00:00.00' union all
    select 8833, 'London', '2017-02-03 01:00:00.00' union all
    select 8833, 'Berlin', '2017-02-03 22:00:00.00' union all
    select 9966, 'Paris', '2017-02-03 04:00:00.00' union all
    select 9966, 'London', '2017-02-04 06:00:00.00' union all
    select 9966, 'Berlin', '2017-02-05 01:00:00.00'
)
, arrivals as (
    select
        id, city, arrival,
        row_number() over (partition by id order by arrival) as rn
    from raw_data
)
, flies as (
    select
        fr.id,
        fr.city as [from],
        fr.arrival as [departure],
        [to].city as [to],
        [to].arrival as [arrival]
    from arrivals fr
    join arrivals [to] on
        fr.id = [to].id
        and [to].rn = fr.rn + 1
)
select
    *
from flies

SQL Server 2012 及更高版本

但如果你有 Sql Server 2012 及更高版本,你可以使用lag 函数来解决它,它可以访问当前的前一行。

例如lag(colX, N, <default>) 表示您获得了 colX 列的第 N 个先前值或值(如果没有)。正是我们需要的!严格来说是上一个:

;with raw_data (id, city, arrival) as (
    -- omitted for the sake of shortness :)
)
, flies as (
    select
        id,
        -- here it is!!! prev city and arrival
        lag(city, 1, null) over (partition by id order by arrival) as [from],
        lag(arrival, 1, null) over (partition by id order by arrival) as [departure],
        city as [to],
        arrival as [arrival]
    from raw_data
)
select
    *
from flies
where
    -- and here we take only rows from where we're 'departured'
    [from] is not null

【讨论】:

  • 我要写什么——为了简短而省略
  • @Katalo 与第一次查询相同的部分 - 填充示例数据。它只是这么多重复的行,所以我省略了它们。很抱歉重命名你原来的列 - 只是匆忙错过了(
【解决方案3】:

您可以使用ROW_NUMBER() 在 CTE 中为每个旅客的签到分配序列号,然后将 CTE 连接到自身以创建结果:

declare @t table (Traveller int not null, checkin varchar(19) not null,
                  TimeStamp datetime2 not null)
insert into @t (Traveller,checkin,TimeStamp) values
(5566,'Madrid',   '2017-06-02T07:56:01'),
(5566,'Barcelona','2017-06-02T07:56:02'),
(5566,'Berlin',   '2017-06-02T07:56:03'),
(5566,'Paris',    '2017-06-02T07:56:04'),
(5566,'London',   '2017-06-02T07:56:05'),
(5566,'Madrid',   '2017-06-02T07:56:06'),
(4422,'Moscow',   '2017-06-02T07:56:07'),
(4422,'Madrid',   '2017-06-02T07:56:08'),
(4422,'Barcelona','2017-06-02T07:56:09'),
(8833,'Barcelona','2017-06-02T07:56:10'),
(8833,'Berlin',   '2017-06-02T07:56:11'),
(8833,'London',   '2017-06-02T07:56:12'),
(8833,'Berlin',   '2017-06-02T07:56:13'),
(9966,'Paris',    '2017-06-02T07:56:14'),
(9966,'London',   '2017-06-02T07:56:15'),
(9966,'Berlin',   '2017-06-02T07:56:16')

;With Numbered as (
    select
        *,
        ROW_NUMBER() OVER (PARTITION BY Traveller ORDER by TimeStamp) as rn
    from @t
)
select
    n1.Traveller,n1.checkin,n2.checkin
from
    Numbered n1
        inner join
    Numbered n2
        on
            n1.Traveller = n2.Traveller and
            n1.rn = n2.rn - 1
order by
    n1.Traveller,n1.rn

结果:

Traveller   checkin             checkin
----------- ------------------- -------------------
4422        Moscow              Madrid
4422        Madrid              Barcelona
5566        Madrid              Barcelona
5566        Barcelona           Berlin
5566        Berlin              Paris
5566        Paris               London
5566        London              Madrid
8833        Barcelona           Berlin
8833        Berlin              London
8833        London              Berlin
9966        Paris               London
9966        London              Berlin

【讨论】:

  • ROW_NUMBER 不是 2012 年的函数吗?在这种情况下为什么不使用 LEAD?
  • @PanagiotisKanavos - 不,ROW_NUMBER() 至少早在 2008 年就已经存在。试图记住它是否是在 2005 年。
  • 文档不再提及 2012 年之前的版本。您有链接吗?
  • @PanagiotisKanavos - 这是一个问题asking about it's use in 2005
  • @PanagiotisKanavos - 只需在我们的一台服务器上运行它 - select top 1 @@VERSION,ROW_NUMBER() OVER (PARTITION BY Name ORDER BY object_id) from sys.objects - 结果 - Microsoft SQL Server 2005 - 9.00.5057.00 (X64), 1
猜你喜欢
  • 2019-03-09
  • 2016-04-27
  • 2018-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-19
相关资源
最近更新 更多