2012-11-15 26 views
9

Tôi đã vật lộn với điều này trong ba ngày đầy đủ bây giờ và tôi không thể có được đầu của tôi xung quanh này. Nó khá giống với một bài viết gần đây của tôi “t-sql tuần tự tuần tự”, nhưng không chính xác giống nhau… Tôi muốn đặt lại số hàng dựa trên thay đổi trong cột x (trong trường hợp của tôi, cột “who”)…T-sql Đặt lại số hàng trên thay đổi trường

Dưới đây là các truy vấn đầu tiên mà trả về một mẫu nhỏ của thô (ish) dữ liệu:

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 

Kết quả:

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 

bước thứ hai là thêm số lượng hàng (tôi đã không làm điều này trong truy vấn đầu tiên vì sử dụng từ DISTINCT); như vậy ...

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 

Kết quả:

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 

Bước tiếp theo là nơi tôi đang gặp khó khăn: mục tiêu là để thiết lập lại các ROWID tới 1 trên mỗi sự thay đổi của giá trị trong “ai” cột. Các mã sau đây được một “gần như có” kết quả (và nó nên được lưu ý rằng tôi đã đánh cắp/mượn mã này từ nơi nào đó, nhưng bây giờ tôi không thể tìm thấy trang 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 

Kết quả:

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.) 

kết quả mong muốn:

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 

Bất kỳ trợ giúp được đánh giá cao. Bạn cũng có thể cười khúc khích với tôi, bởi vì tôi chắc chắn điều này khá đơn giản để giải quyết - tôi dường như không thể thoát ra khỏi con đường của mình.

Cảm ơn.

Trả lời

1

Thay vì:

PARTITION BY custno ORDER BY custno, moddate, who) 

thử:

PARTITION BY custno, who ORDER BY custno, moddate) 
+0

Đã thử rằng đã có (có vẻ như nó sẽ hoạt động, phải không?) Nhưng nó vẫn trả về cùng một tập hợp kết quả với số hàng cuối cùng = 14. Tuy nhiên, hãy giữ ý tưởng về tôi. Tôi chỉ thiếu một số bước đơn giản, tôi chắc chắn. – Kris

-1

Giải pháp duy nhất tôi có thể nghĩ đến là sử dụng một con trỏ (ugh) và đau khổ trong suốt quá trình RBAR. KHÔNG phải là giải pháp thanh lịch vì con trỏ sẽ phải đọc vượt quá 1m hàng. Rất tiếc.

18

Nếu bạn đang sử dụng SQL Server 2012, bạn có thể sử dụng LAG để so sánh giá trị với hàng trước đó và bạn có thể sử dụng SUMOVER để ghi lại các thay đổi.

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 Fiddle

Cập nhật:

Một phiên bản dành cho SQL Server 2005. Nó sử dụng một CTE đệ quy và một bảng tạm để lưu trữ trung gian của các dữ liệu bạn cần để lặp qua.

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 Fiddle

+0

Thật không may, chúng tôi vẫn đang chạy năm 2005 với kế hoạch nâng cấp lên 2008R2 vào tháng tới. Hàm LAG sẽ là một giải pháp sạch cho vấn đề này. Cảm ơn bạn đã mang nó đến sự chú ý của tôi ... chỉ là một lý lẽ khác để chuyển sang SQL Server 2012. – Kris

+0

Giải pháp rất hay. Mối quan tâm duy nhất của tôi (và nó không được thành lập trên thực tế) là việc sử dụng 0 với tối đa. Điều đó có thể gây ra một vòng lặp vô hạn, nhưng trong suy nghĩ về cấu trúc dữ liệu của tôi, có lẽ không phải với dữ liệu tôi có. Cảm ơn bạn!!! – Kris

+0

@Kris Bạn được chào đón. –

4

Tôi đã thành công với vấn đề này bằng cách sử dụng Rank():

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

này trả lại kết quả mong muốn của bạn. Tôi thực sự tìm thấy bài đăng này đang cố gắng giải quyết cùng một vấn đề.

Các vấn đề liên quan