2012-06-20 43 views
21

Hãy nói rằng tôi có bảng sau:Chọn hàng nơi giá trị cột đã thay đổi

Value Time 
0  15/06/2012 8:03:43 PM 
1  15/06/2012 8:03:43 PM  * 
1  15/06/2012 8:03:48 PM 
1  15/06/2012 8:03:53 PM 
1  15/06/2012 8:03:58 PM  
2  15/06/2012 8:04:03 PM  * 
2  15/06/2012 8:04:08 PM 
3  15/06/2012 8:04:13 PM  * 
3  15/06/2012 8:04:18 PM 
3  15/06/2012 8:04:23 PM 
2  15/06/2012 8:04:28 PM  * 
2  15/06/2012 8:04:33 PM  

Làm thế nào để chọn các hàng đóng vai chính, có nghĩa là, nơi Value đã thay đổi? Về cơ bản tôi đang cố gắng tìm thời gian khi Value đã thay đổi để tôi có thể thực hiện các truy vấn khác dựa trên các khoảng thời gian đó. Giải pháp không nên phụ thuộc vào việc biết trước Value hoặc Time.

Dường như với tôi rằng điều này không phải là rất khó (nhưng nó đủ khó cho tôi rõ ràng!).

Tôi hiện đang sử dụng SQL Server 2008 mặc dù tôi có quyền truy cập vào năm 2012 nếu các chức năng cửa sổ/phân tích mới hữu ích.

Tôi đã thử điều chỉnh các giải pháp ở đây http://blog.sqlauthority.com/2011/11/24/sql-server-solution-to-puzzle-simulate-lead-and-lag-without-using-sql-server-2012-analytic-function/ nhưng truy vấn của tôi không hoàn thành sau một giờ! Tôi nghĩ rằng sự gia nhập nổ kích thước hàng để một cái gì đó không thể quản lý (hoặc tôi screwed nó lên).

Tôi có thể giải quyết vấn đề này bằng mã C# và nhiều cuộc gọi db, nhưng có vẻ như có thể thực hiện một hàm có giá trị bảng hoặc SP sẽ đẹp hơn nhiều.

Ngoài ra, giải pháp chỉ hoạt động khi tăng Value là OK nếu điều đó dễ dàng hơn.

Trả lời

27

Tôi nghĩ rằng đây là những gì bạn đang sau:

;WITH x AS 
(
    SELECT value, time, rn = ROW_NUMBER() OVER 
    (PARTITION BY Value ORDER BY Time) 
    FROM dbo.table 
) 
SELECT * FROM x WHERE rn = 1; 

Điều này có thể được làm chậm nếu resultset là lớn và không có một chỉ số hỗ trợ tốt ...

EDIT

Ah, chờ một giây, các giá trị tăng lên và xuống, không chỉ lên ... nếu đó là trường hợp bạn có thể thử cách tiếp cận này chậm hơn nhiều:

DECLARE @x TABLE(value INT, [time] DATETIME) 

INSERT @x VALUES 
(0,'20120615 8:03:43 PM'),-- 
(1,'20120615 8:03:43 PM'),--* 
(1,'20120615 8:03:48 PM'),-- 
(1,'20120615 8:03:53 PM'),-- 
(1,'20120615 8:03:58 PM'),-- 
(2,'20120615 8:04:03 PM'),--* 
(2,'20120615 8:04:08 PM'),-- 
(3,'20120615 8:04:13 PM'),--* 
(3,'20120615 8:04:18 PM'),-- 
(3,'20120615 8:04:23 PM'),-- 
(2,'20120615 8:04:28 PM'),--* 
(2,'20120615 8:04:33 PM'); 

;WITH x AS 
(
    SELECT *, rn = ROW_NUMBER() OVER (ORDER BY time) 
    FROM @x 
) 
SELECT x.value, x.[time] 
FROM x LEFT OUTER JOIN x AS y 
ON x.rn = y.rn + 1 
AND x.value <> y.value 
WHERE y.value IS NOT NULL; 

Kết quả:

value time 
----- ----------------------- 
1  2012-06-15 20:03:43.000 
2  2012-06-15 20:04:03.000 
3  2012-06-15 20:04:13.000 
2  2012-06-15 20:04:28.000 
+0

Thật tuyệt vời nhanh. Tăng giá trị là OK. Tôi sẽ thử nó. – agentnega

+0

Tuyệt vời, cảm ơn @Aaron Bertrand. Trên dữ liệu của tôi phiên bản thứ hai mất ít hơn gấp đôi thời gian của phiên bản đầu tiên, do đó, sự khác biệt hiệu suất không phải là quá xấu. – agentnega

+0

Làm thế nào để chuyển đổi truy vấn này cũng bao gồm một ID (cột mới) trong kết quả đầu ra? – ThinkCode

12
DECLARE @x TABLE(value INT, [time] DATETIME) 

INSERT @x VALUES 
(0,'20120615 8:03:43 PM'),-- 
(1,'20120615 8:03:43 PM'),--* 
(1,'20120615 8:03:48 PM'),-- 
(1,'20120615 8:03:53 PM'),-- 
(1,'20120615 8:03:58 PM'),-- 
(2,'20120615 8:04:03 PM'),--* 
(2,'20120615 8:04:08 PM'),-- 
(3,'20120615 8:04:13 PM'),--* 
(3,'20120615 8:04:18 PM'),-- 
(3,'20120615 8:04:23 PM'),-- 
(2,'20120615 8:04:28 PM'),--* 
(2,'20120615 8:04:33 PM'); 


; with temp as 
(
SELECT 
    value, [time], lag(value,1,-1) over (order by [time]) as lastValue 
FROM @x 
) 
SELECT 
    [value],[time] 
FROM 
    temp 
WHERE value <> lastValue 

Kết quả:

value time 
--------------------------- 
0 2012-06-15 20:03:43.000 
1 2012-06-15 20:03:43.000 
2 2012-06-15 20:04:03.000 
3 2012-06-15 20:04:13.000 
2 2012-06-15 20:04:28.000 
+3

Gọn gàng, nhưng đáng nói đến điều này chỉ được hỗ trợ từ SQL 2012 trở đi. –

+0

Đây là siêu nhanh! Truy vấn cho các phiên bản trước đã hoạt động 11 phút và sau đó tôi phải hủy nó. –

1

Chúng ta có thể làm điều này bằng phụ truy vấn cũng

SELECT sub1.value, sub1.time FROM 
    (SELECT *,rn,id FROM 
    (SELECT *,row_number() over (partition by value order by time) AS rn, row_number() over (order by time) AS id FROM x) order by time) sub1 
    LEFT OUTER JOIN 
    (SELECT *,rn,id FROM 
    (SELECT *,row_number() over (partition by value order by time) AS rn, row_number() over (order by time) AS id FROM x) order by time) sub2 
    ON sub1.id = sub2.id + 1 
    WHERE sub1.rn - sub2.rn <> 1 OR sub2.rn IS NULL; 

Vì vậy, tôi đã so sánh các giá trị của 2 hàng nếu nó thay đổi thì sự khác biệt của rn sẽ không bằng 1 nếu không rn giá trị sẽ tăng thêm 1 vì vậy, tôi đã chọn tất cả các hàng có sự khác biệt với giá trị rn của hàng tiếp theo không phải là 1 và sub2.rn IS NULL được sử dụng cho hàng đầu tiên vì tham gia sẽ xảy ra từ id = 2.

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