web-dev-qa-db-ja.com

T-sqlフィールド変更時に行番号をリセット

私はこれに丸3日間苦労しましたが、頭を悩ませることはできません。これは私の最近の投稿「t-sql順次期間」と非常に似ていますが、まったく同じではありません…列x(私の場合は列「who」)の変更に基づいて行番号をリセットしたい…

生(ish)データの小さなサンプルを返す最初のクエリは次のとおりです。

SELECT      DISTINCT chr.custno, 
            CAST(LEFT(CONVERT( VARCHAR(20),chr.moddate,112),10)+ ' ' + chr.modtime AS DATETIME)as  moddate, 
            chr.who     
FROM        <TABLE> chr 
WHERE       chr.custno = 581827
            AND LEFT(chr.who, 5) = 'EMSZC'
            AND chr.[description] NOT LIKE 'Recalled and viewed this customer'
ORDER BY    chr.custno

結果:

custno      moddate             who
581827      2012-11-08 08:38:00.000     EMSZC14
581827      2012-11-08 08:41:10.000     EMSZC14
581827      2012-11-08 08:53:46.000     EMSZC14
581827      2012-11-08 08:57:04.000     EMSZC14
581827      2012-11-08 08:58:35.000     EMSZC14
581827      2012-11-08 08:59:13.000     EMSZC14
581827      2012-11-08 09:00:06.000     EMSZC14
581827      2012-11-08 09:04:39.000     EMSZC49 Reset row number to 1
581827      2012-11-08 09:05:04.000     EMSZC49
581827      2012-11-08 09:06:32.000     EMSZC49
581827      2012-11-08 09:12:03.000     EMSZC49
581827      2012-11-08 09:12:38.000     EMSZC49
581827      2012-11-08 09:14:18.000     EMSZC49
581827      2012-11-08 09:17:35.000     EMSZC14 Reset row number to 1

2番目のステップは、行番号を追加することです(Word DISTINCTを使用しているため、最初のクエリではこれを行いませんでした)。そう…

WITH c1 AS (
        SELECT      DISTINCT chr.custno
                    CAST(LEFT(CONVERT( VARCHAR(20),chr.moddate,112),10)+ ' ' + chr.modtime AS DATETIME)as moddate,
                    chr.who
        FROM        <TABLE> chr 
        WHERE       chr.custno = 581827
                    AND LEFT(chr.who, 5) = 'EMSZC'
                    AND chr.[description] NOT LIKE 'Recalled and viewed this customer'
        )

SELECT  ROW_NUMBER() OVER (PARTITION BY custno ORDER BY custno, moddate, who) AS RowID, custno, moddate, who
FROM    c1

結果:

RowID   custno      moddate                      who
1       581827      2012-11-08 08:38:00.000     EMSZC14
2       581827      2012-11-08 08:41:10.000     EMSZC14
3       581827      2012-11-08 08:53:46.000     EMSZC14
4       581827      2012-11-08 08:57:04.000     EMSZC14
5       581827      2012-11-08 08:58:35.000     EMSZC14
6       581827      2012-11-08 08:59:13.000     EMSZC14
7       581827      2012-11-08 09:00:06.000     EMSZC14
8       581827      2012-11-08 09:04:39.000     EMSZC49 Reset row number to 1
9       581827      2012-11-08 09:05:04.000     EMSZC49
10      581827      2012-11-08 09:06:32.000     EMSZC49
11      581827      2012-11-08 09:12:03.000     EMSZC49
12      581827      2012-11-08 09:12:38.000     EMSZC49
13      581827      2012-11-08 09:14:18.000     EMSZC49
14      581827      2012-11-08 09:17:35.000     EMSZC14 Reset row number to 1

次のステップは、私が行き詰まっているところです。目標は、「who」列の値が変更されるたびにRowIDを1にリセットすることです。次のコードは「ほぼそこにある」という結果になります(このコードをどこかから盗んだり借りたりしたのですが、今ではWebサイトが見つかりません)。

WITH c1 AS (
        SELECT      DISTINCT chr.custno,
                    CAST(LEFT(CONVERT( VARCHAR(20),chr.moddate,112),10)+ ' ' + chr.modtime AS DATETIME)as moddate,
                    chr.who
        FROM        <TABLE> chr 
        WHERE       chr.custno = 581827
                    AND LEFT(chr.who, 5) = 'EMSZC'
                    AND chr.[description] NOT LIKE 'Recalled and viewed this customer'
        )
, c1a AS    (
            SELECT  ROW_NUMBER() OVER (PARTITION BY custno ORDER BY custno, moddate, who) AS RowID, custno, moddate, who
            FROM    c1
            )

SELECT  x.RowID - y.MinID + 1 AS Row,
        x.custno, x.Touch, x.moddate, x.who      
FROM    (
            SELECT  custno, who, MIN(RowID) AS MinID
            FROM    c1a
            GROUP BY custno, who
        ) AS y
        INNER JOIN c1a x ON x.custno = y.custno AND x.who = y.who

結果:

Row custno      moddate                    who
1   581827      2012-11-08 08:38:00.000     EMSZC14
2   581827      2012-11-08 08:41:10.000     EMSZC14
3   581827      2012-11-08 08:53:46.000     EMSZC14
4   581827      2012-11-08 08:57:04.000     EMSZC14
5   581827      2012-11-08 08:58:35.000     EMSZC14
6   581827      2012-11-08 08:59:13.000     EMSZC14
7   581827      2012-11-08 09:00:06.000     EMSZC14
1   581827      2012-11-08 09:04:39.000     EMSZC49 Reset row number to 1 (Hooray! It worked!)
2   581827      2012-11-08 09:05:04.000     EMSZC49
3   581827      2012-11-08 09:06:32.000     EMSZC49
4   581827      2012-11-08 09:12:03.000     EMSZC49
5   581827      2012-11-08 09:12:38.000     EMSZC49
6   581827      2012-11-08 09:14:18.000     EMSZC49
14  581827      2012-11-08 09:17:35.000     EMSZC14 Reset row number to 1 (Crappies.)

望ましい結果:

Row custno      moddate                     who
1   581827      2012-11-08 08:38:00.000     EMSZC14
2   581827      2012-11-08 08:41:10.000     EMSZC14
3   581827      2012-11-08 08:53:46.000     EMSZC14
4   581827      2012-11-08 08:57:04.000     EMSZC14
5   581827      2012-11-08 08:58:35.000     EMSZC14
6   581827      2012-11-08 08:59:13.000     EMSZC14
7   581827      2012-11-08 09:00:06.000     EMSZC14
1   581827      2012-11-08 09:04:39.000     EMSZC49 Reset row number to 1 
2   581827      2012-11-08 09:05:04.000     EMSZC49
3   581827      2012-11-08 09:06:32.000     EMSZC49
4   581827      2012-11-08 09:12:03.000     EMSZC49
5   581827      2012-11-08 09:12:38.000     EMSZC49
6   581827      2012-11-08 09:14:18.000     EMSZC49
1   581827      2012-11-08 09:17:35.000     EMSZC14 Reset row number to 1

どんな援助も大歓迎です。あなたも私をくすくす笑うことができます。なぜなら、これは非常に簡単に解決できると確信しているからです。私は自分のやり方から抜け出すことができないようです。

ありがとう。

11
Kris

SQL Server 2012を使用している場合は、 [〜#〜] lag [〜#〜] を使用して値を前の行と比較し、 [〜#〜] sum [〜 #〜] および [〜#〜] over [〜#〜] 変更を記録します。

with C1 as
(
  select custno,
         moddate,
         who,
         lag(who) over(order by moddate) as lag_who
  from chr
),
C2 as
(
  select custno,
         moddate,
         who,
         sum(case when who = lag_who then 0 else 1 end) 
            over(order by moddate rows unbounded preceding) as change 
  from C1
)
select row_number() over(partition by change order by moddate) as RowID,
       custno,
       moddate,
       who
from C2

SQLフィドル

更新:

SQL Server 2005のバージョン。反復する必要のあるデータの中間ストレージとして、再帰CTEと一時テーブルを使用します。

create table #tmp
(
  id int primary key,
  custno int not null,
  moddate datetime not null,
  who varchar(10) not null
);

insert into #tmp(id, custno, moddate, who)
select row_number() over(order by moddate),
       custno,
       moddate,
       who
from chr;

with C as
(
  select 1 as rowid,
         T.id,
         T.custno,
         T.moddate,
         T.who,
         cast(null as varchar(10)) as lag_who
  from #tmp as T
  where T.id = 1
  union all
  select case when T.who = C.who then C.rowid + 1 else 1 end,
         T.id,
         T.custno,
         T.moddate,
         T.who,
         C.who
  from #tmp as T
    inner join C
      on T.id = C.id + 1
)
select rowid,
       custno,
       moddate,
       who
from C
option (maxrecursion 0);

drop table #tmp;

SQLフィドル

25
Mikael Eriksson

私はRank()を使用してこの問題に成功しました:

SELECT RANK() OVER (PARTITION BY who ORDER BY custno, moddate) AS RANK

これにより、希望する結果が返されました。私は実際にこの投稿が同じ問題を解決しようとしているのを見つけました。

6
Thrifty2100

の代わりに:

PARTITION BY custno ORDER BY custno, moddate, who)

試してください:

PARTITION BY custno, who ORDER BY custno, moddate)
2
JeffO