SQL Server:查询顺序子行

时间:2014-07-13 08:13:46

标签: sql sql-server subquery

我有一张如下表格

╔══════════════════════════════════╗
║  id  ACCT    CAT AMT     MYDATE  ║
╠══════════════════════════════════╣
║ 1   1111    c   200     6/1/2014 ║
║ 2   2121        100     6/1/2014 ║
║ 3   3131        100     6/1/2014 ║
║ 4   2222    c   250     6/2/2014 ║
║ 5   3131        100     6/2/2014 ║
║ 6   2121        100     6/2/2014 ║
║ 7   4141        50      6/2/2014 ║
║ 8   1111    c   350     6/3/2014 ║
║ 9   5151        150     6/3/2014 ║
║ 10  6161        200     6/3/2014 ║
║ 11  3333    c   400     6/3/2014 ║
║ 12  2121        200     6/3/2014 ║
║ 13  3131        200     6/3/2014 ║
║ 14  1111    c   500     6/5/2014 ║
║ 15  4141        100     6/5/2014 ║
║ 16  5151        200     6/5/2014 ║
║ 17  6161        200     6/5/2014 ║
║ 18  2222    c   400     6/5/2014 ║
║ 19  4141        400     6/5/2014 ║
╚══════════════════════════════════╝

CAT ='c'的行是发票,右下方没有CAT ='c'的行是属于每张发票的已售商品。

获取id = 14的项目,我使用以下查询

WITH tbl (id, ACCT, CAT, AMT, MYDATE)
 AS
(
 SELECT           1,    1111,   'c',    200,    '6/1/2014'
 UNION ALL SELECT 2,    2121,   ' ',    100,    '6/1/2014'
 UNION ALL SELECT 3,    3131,   ' ',    100,    '6/1/2014'
 UNION ALL SELECT 4,    2222,   'c',    250,    '6/2/2014'
 UNION ALL SELECT 5,    3131,   ' ',    100,    '6/2/2014'
 UNION ALL SELECT 6,    2121,   ' ',    100,    '6/2/2014'
 UNION ALL SELECT 7,    4141,   ' ',    50,     '6/2/2014'  
 UNION ALL SELECT 8,    1111,   'c',    350,    '6/3/2014'
 UNION ALL SELECT 9,    5151,   ' ',    150,    '6/3/2014'
 UNION ALL SELECT 10,   6161,   ' ',    200,    '6/3/2014'
 UNION ALL SELECT 11,   3333,   'c',    400,    '6/3/2014'
 UNION ALL SELECT 12,   2121,   ' ',    200,    '6/3/2014'
 UNION ALL SELECT 13,   3131,   ' ',    200,    '6/3/2014'
 UNION ALL SELECT 14,   1111,   'c',    500,    '6/5/2014'
 UNION ALL SELECT 15,   4141,   ' ',    100,    '6/5/2014'
 UNION ALL SELECT 16,   5151,   ' ',    200,    '6/5/2014'
 UNION ALL SELECT 17,   6161,   ' ',    200,    '6/5/2014'
 UNION ALL SELECT 18,   2222,   'c',    400,    '6/5/2014'
 UNION ALL SELECT 19,   4141,   ' ',    400,    '6/5/2014'
)
, tid( orgid )
AS
(
    SELECT TOP (1) id FROM tbl WHERE MYDATE = '6/5/2014' and ACCT = 1111 and AMT = 500
)
SELECT * from tbl
INNER JOIN tid on tbl.id > tid.orgid
INNER JOIN (SELECT ISNULL ( MIN ( id ),  (SELECT MAX ( id ) + 1 from tbl ) ) AS nextnid FROM tbl
WHERE id > ( SELECT id from tid ) AND CAT = 'c' ) as t2 on tbl.id  < t2.nextnidid  < t2.nextnid

RESULT

╔════════╦═════════╦═══════╦═══════════╦═════════╦════════╗
║ id     ║ ACCT    ║  AMT  ║ MYDATE    ║ orgid   ║ nextid ║
╠════════╬═════════╬═══════╬═══════════╬═════════╬════════╣
║     15 ║    4141 ║   100 ║ 6/5/2014  ║      14 ║     18 ║
║     16 ║    5151 ║   200 ║ 6/5/2014  ║      14 ║     18 ║
║     17 ║    6161 ║   200 ║ 6/5/2014  ║      14 ║     18 ║
╚════════╩═════════╩═══════╩═══════════╩═════════╩════════╝

现在,我的问题是..如何查询ACCT = 1111和CREDIT ='c'的所有子项目行?

Result should be something like this
╔══════════╦══════════════╦══════╦════════════╦═════════╦════════╗
║     id   ║ ACCT         ║ AMT  ║ MYDATE     ║ orgid   ║ nextid ║
╠══════════╬══════════════╬══════╬════════════╬═════════╬════════╣
║        2 ║         2121 ║  100 ║ 6/1/2014   ║    1    ║      4 ║
║        3 ║         3131 ║  100 ║ 6/1/2014   ║    1    ║      4 ║
║        9 ║         5151 ║  150 ║ 6/3/2014   ║    8    ║     11 ║
║       10 ║         6161 ║  200 ║ 6/3/2014   ║    8    ║     11 ║
║       15 ║         4141 ║  100 ║ 6/5/2014   ║    14   ║     18 ║
║       16 ║         5151 ║  200 ║ 6/5/2014   ║    14   ║     18 ║
║       17 ║         6161 ║  200 ║ 6/5/2014   ║    14   ║     18 ║
╚══════════╩══════════════╩══════╩════════════╩═════════╩════════╝

2 个答案:

答案 0 :(得分:0)

在SQL Server 2005中,只有有限的窗口聚合函数支持,所以这混合了旧式语法加上MIN OVER:

WITH tbl (id, ACCT, CAT, AMT, MYDATE)
 AS
(
 SELECT           1,    1111,   'c',    200,    '6/1/2014'
 UNION ALL SELECT 2,    2121,   ' ',    100,    '6/1/2014'
 UNION ALL SELECT 3,    3131,   ' ',    100,    '6/1/2014'
 UNION ALL SELECT 4,    2222,   'c',    250,    '6/2/2014'
 UNION ALL SELECT 5,    3131,   ' ',    100,    '6/2/2014'
 UNION ALL SELECT 6,    2121,   ' ',    100,    '6/2/2014'
 UNION ALL SELECT 7,    4141,   ' ',    50,     '6/2/2014'  
 UNION ALL SELECT 8,    1111,   'c',    350,    '6/3/2014'
 UNION ALL SELECT 9,    5151,   ' ',    150,    '6/3/2014'
 UNION ALL SELECT 10,   6161,   ' ',    200,    '6/3/2014'
 UNION ALL SELECT 11,   3333,   'c',    400,    '6/3/2014'
 UNION ALL SELECT 12,   2121,   ' ',    200,    '6/3/2014'
 UNION ALL SELECT 13,   3131,   ' ',    200,    '6/3/2014'
 UNION ALL SELECT 14,   1111,   'c',    500,    '6/5/2014'
 UNION ALL SELECT 15,   4141,   ' ',    100,    '6/5/2014'
 UNION ALL SELECT 16,   5151,   ' ',    200,    '6/5/2014'
 UNION ALL SELECT 17,   6161,   ' ',    200,    '6/5/2014'
 UNION ALL SELECT 18,   2222,   'c',    400,    '6/5/2014'
 UNION ALL SELECT 19,   4141,   ' ',    400,    '6/5/2014'
)
,cte1 as
 ( 
   select
      t1.*,
      -- find the id of the next invoice id
      -- all rows for an invoice share the same id
      ( select min(t2.id) 
        from tbl as t2 
        where t2.id > t1.id
          and CAT = 'c') as nextId
   from tbl as t1
 )
,cte2 as
 ( 
   select
      cte1.*,
      -- assign original invoice id/acct to each item
      min(case when CAT = 'c' then id   end) over (partition by nextId) as orgId,
      min(case when CAT = 'c' then acct end) over (partition by nextId) as orgAcct
   from cte1
 )
select * 
from cte2
where orgAcct = 1111
and id <> orgId

答案 1 :(得分:0)

JFYI,我在另一个论坛上问道,发现这个有点儿比较快

WITH tbl(id, ACCT, CAT, AMT, D)
AS
(
           SELECT 1,    1111,   'c',    200,    '6/1/2014'
 UNION ALL SELECT 2,    2121,   ' ',    100,    '6/1/2014'
 UNION ALL SELECT 3,    3131,   ' ',    100,    '6/1/2014'
 UNION ALL SELECT 4,    2222,   'c',    250,    '6/2/2014'
 UNION ALL SELECT 5,    3131,   ' ',    100,    '6/2/2014'
 UNION ALL SELECT 6,    2121,   ' ',    100,    '6/2/2014'
 UNION ALL SELECT 7,    4141,   ' ',     50,    '6/2/2014'  
 UNION ALL SELECT 8,    1111,   'c',    350,    '6/3/2014'
 UNION ALL SELECT 9,    5151,   ' ',    150,    '6/3/2014'
 UNION ALL SELECT 10,   6161,   ' ',    200,    '6/3/2014'
 UNION ALL SELECT 11,   3333,   'c',    400,    '6/3/2014'
 UNION ALL SELECT 12,   2121,   ' ',    200,    '6/3/2014'
 UNION ALL SELECT 13,   3131,   ' ',    200,    '6/3/2014'
 UNION ALL SELECT 14,   1111,   'c',    500,    '6/5/2014'
 UNION ALL SELECT 15,   4141,   ' ',    100,    '6/5/2014'
 UNION ALL SELECT 16,   5151,   ' ',    200,    '6/5/2014'
 UNION ALL SELECT 17,   6161,   ' ',    200,    '6/5/2014'
 UNION ALL SELECT 18,   2222,   'c',    400,    '6/5/2014'
 UNION ALL SELECT 19,   4141,   ' ',    400,    '6/5/2014'
)
, cte1
AS
(
    SELECT * , CASE WHEN CAT = 'C' THEN id ELSE ( SELECT MAX( id ) FROM tbl AS aa 
    WHERE CAT = 'C' AND aa.id < tbl.id ) END AS orgId
    FROM tbl 
)
SELECT cc.* FROM cte1 AS bb
INNER JOIN cte1 as cc ON bb.id = cc.orgId AND cc.CAT = ' '
WHERE bb.ACCT = 1111 AND bb.CAT = 'C'