在T-SQL SQL Server 2008中基于另一行分配行号

时间:2013-05-20 17:23:14

标签: sql-server

我需要一个select查询来根据订单交易表中的前一行编号来生成新项目。

有序列LineItem,因此ID与01,02,03等之间的差异会有所不同。我正在考虑查询中的临时表:OrderNo,ID其中ID为01到20以及用于测试LineItem的case语句以及它是否在......之间...

ID 01 2834111和ID CA800 = 2834112更多01而不是02,所以-01将是它的前缀。

我是否在正确的轨道上?这似乎是一个长期的陈述。

OrderNo    LineItem    ID     Need to Make
----------------------------------------------------------------------     
236201    2834111    01           
236201    2834112    CA800                       01-CA800
236201    2834113    BERRY    01-BERRY
236201    2834114    02           
236201    2834115    MSIJ54    02-IJ54
236201    2834116    92-D-06    02-92-D-06
236201    2834117    BERRY    02-BERRY
236201    2834118    03           
236201    2834119    SACOLBS24    03-SACOLBS24
236201    2834121                                   OWK32    03-OWK32
236201    2834122                                   04               
236201    2834123                                    SBMRY    04-SBMRY
236201    2834124                                    DAWN    04-DAWN
236201    2834125                                    05               
236201    2834126                                   NWM216D     05-NWM216D
236201    2834127                                    DAWN     05-DAWN

3 个答案:

答案 0 :(得分:1)

如果HABO有您的要求,您可以试试这个:

SELECT
    p.*, id.id + '- ' + p.id AS prefixedid
FROM
    (SELECT 
        tbl.orderno,
        tbl.lineitem,
        tbl.id, 
        MAX(id.lineitem) AS max_id_lineitem
    FROM
        tbl INNER JOIN
        (SELECT * FROM tbl WHERE id >='01' AND id <='20') id ON
        tbl.orderno = id.orderno AND
        tbl.lineitem > id.lineitem LEFT OUTER JOIN
        (SELECT * FROM tbl WHERE id >='01' AND id <='20') id_fornull ON
        tbl.orderno = id_fornull.orderno AND
        tbl.lineitem = id_fornull.lineitem
    WHERE
        id_fornull.lineitem IS NULL
    GROUP BY
        tbl.orderno,
        tbl.lineitem,
        tbl.id) p INNER JOIN
    (SELECT * FROM tbl WHERE id >='01' AND id <='20') id ON
    p.orderno = id.orderno AND
    p. max_id_lineitem = id.lineitem

答案 1 :(得分:0)

首先将关键行拉入临时表:

SELECT OrderNo, LineItem, ID
INTO #TempOrder
FROM YourOrderTable
WHERE SUBSTRING(LINEITEM, 1, 1) IN ('1','2')
AND SUBSTRING(LINEITEM, 2, 1) IN ('1','2','3','4','5','6','7','8','9','0')
AND LEN(LINEITEM) = 2

然后,如果您正在寻找特定的@LineItem:

SELECT @MaxLineItem = MAX(LineItem)
FROM #TempOrder
WHERE LineItem <= @LineItem

然后你可以查找:

SELECT @MaxLineItemID = ID
FROM #TempOrder
WHERE LineItem = @MaxLineItem

@MaxLineItem必须与@LineItem不同吗?如果是这样的话:

SELECT CASE WHEN @LineItem = @MaxLineItem THEN ID
  ELSE @MaxLineItemID + ' - ' + ID END AS NewID
FROM YourOrderTable
WHERE LineItem = @LineItem

答案 2 :(得分:0)

您可能希望在Row_Number上对OrderNo进行分区,但这应该可以让您入门:

declare @LineItems as Table ( LineItem Int Identity, Id VarChar(16) );
insert into @LineItems ( Id ) values
  ( '01' ), ( 'CA800' ), ( 'BERRY' ),
  ( '02' ), ( 'MSIJ54' ), ( '92-D-06' ), ( 'BERRY' );

with LineItems as (
  select LineItem, Id, Row_Number() over ( order by LineItem ) as RN
    from @LineItems
  ),
  PrefixedLineItems as (
  select LineItem, Id, Id as Prefix, Cast( NULL as VarChar(16) ) as Result, RN
    from LineItems
    where RN = 1
  union all
  select LI.LineItem, LI.Id,
    case when IsNumeric( LI.Id ) = 1 then LI.Id else PLI.Prefix end,
    case when IsNumeric( LI.Id ) = 1 then NULL else Cast( PLI.Prefix + '-' + LI.Id as VarChar(16) ) end, LI.RN
    from PrefixedLineItems as PLI inner join
      LineItems as LI on LI.RN = PLI.RN + 1
  )
  select LineItem, Id, Result
    from PrefixedLineItems
    order by LineItem
    option ( MaxRecursion 0 )

如果重要的 ID值必须从01开始并计算到20,则可以添加其他测试。

编辑:嗯,这种情况很糟糕。

生成其他样本数据的版本是:

-- Sample data.
if Object_Id( 'tempdb..#LineItems', N'U' ) is not NULL
  drop table #LineItems;
create table #LineItems ( LineItem Int Identity Primary Key, Id VarChar(32) not null );

insert into #LineItems ( Id ) values
  ( '01' ), ( 'CA800' ), ( 'BERRY' ),
  ( '02' ), ( 'MSIJ54' ), ( '92-D-06' ), ( 'BERRY' );

-- Generate some additional sample data.
declare @Count as Int = 1000;
while @Count > 0
  begin
  if Rand() < 0.1 -- Make about 10% of the entries numeric.
    insert into #LineItems ( Id ) values ( Right( '0' + Cast( Floor( Rand() * 20 ) + 1 as VarChar(2) ), 2 ) );
  else
    insert into #LineItems ( Id ) values ( 'Foo ' + Cast( Rand() as VarChar(16) ) );
  set @Count = @Count - 1;
  end;

-- Time the query.
declare @Start as DateTime = SysDateTime();
with NumberedLineItems as (
  select LineItem, Id, Row_Number() over ( order by LineItem ) as RN
    from #LineItems
  ),
  PrefixedLineItems as (
  select LineItem, Id, Id as Prefix, Cast( NULL as VarChar(16) ) as Result, RN
    from NumberedLineItems
    where RN = 1
  union all
  select LI.LineItem, LI.Id,
    case when IsNumeric( LI.Id ) = 1 then LI.Id else PLI.Prefix end,
    case when IsNumeric( LI.Id ) = 1 then NULL else Cast( PLI.Prefix + '-' + LI.Id as VarChar(16) ) end, LI.RN
    from PrefixedLineItems as PLI inner join
      NumberedLineItems as LI on LI.RN = PLI.RN + 1
  )
  select LineItem, Id, Result
    from PrefixedLineItems
    order by LineItem
    option ( MaxRecursion 0 );
select DateDiff( ms, @Start, SysDateTime() ) as [Elapsed Milliseconds],
  ( select Count(*) from #LineItems ) as [Rows];

这种RBAR方法可以更有效地扩展:

-- Sample data.
if Object_Id( 'tempdb..#LineItems', N'U' ) is not NULL
  drop table #LineItems;
create table #LineItems ( LineItem Int Identity Primary Key, Id VarChar(32) not null );

insert into #LineItems ( Id ) values
  ( '01' ), ( 'CA800' ), ( 'BERRY' ),
  ( '02' ), ( 'MSIJ54' ), ( '92-D-06' ), ( 'BERRY' );

-- Generate some additional sample data.
declare @Count as Int = 10000;
while @Count > 0
  begin
  if Rand() < 0.1 -- Make about 10% of the entries numeric.
    insert into #LineItems ( Id ) values
      ( Right( '0' + Cast( Floor( Rand() * 20 ) + 1 as VarChar(2) ), 2 ) )
  else
    insert into #LineItems ( Id ) values ( 'Foo ' + Cast( Rand() as VarChar(16) ) )
  set @Count = @Count - 1;
  end;

-- Create a fiendish thingy.
declare Plod cursor fast_forward for
  select LineItem, Id
    from #LineItems
    order by LineItem;
declare @LineItem as Int;
declare @Id as VarChar(32);
declare @Prefix as VarChar(2)

-- Create a table to hold the results.
if Object_Id( 'tempdb..#PrefixedLineItems', N'U' ) is not NULL
  drop table #PrefixedLineItems;
create table #PrefixedLineItems
  ( LineItem Int Primary Key, Id VarChar(32) not null, Prefix VarChar(2) null );

-- RBAR.
declare @Start as DateTime = SysDateTime();
open Plod;
fetch next from Plod into @LineItem, @Id;
while @@Fetch_Status = 0
  begin
  set @Prefix = case when IsNumeric( @Id ) = 1 then @Id else @Prefix end;
  insert into #PrefixedLineItems ( LineItem, Id, Prefix ) values
    ( @LineItem, @Id, case when IsNumeric( @Id ) = 1 then NULL else @Prefix end );
  fetch next from Plod into @LineItem, @Id;
  end;
close Plod;
deallocate Plod;

-- That's all, folks!
select LineItem, Id, Prefix
  from #PrefixedLineItems
  order by LineItem;
select DateDiff( ms, @Start, SysDateTime() ) as [Elapsed Milliseconds],
  ( select Count(*) from #LineItems ) as [Rows]