2013-02-26 34 views
7

Tôi có một bảng với các lược đồ sau:Tạo một truy vấn để lấp đầy những khoảng trống trong một bảng do dữ liệu xấu

DateTime [Creation Date] PK 
int [Data] 

Dữ liệu cột có giá trị đến từ một cảm biến, một cái gì đó như thế này:

123 
225 
354 
578 
0 
2151 
2331 
0 
2555 
2678 

Như bạn có thể thấy giá trị luôn tăng.

Do sự cố trong cảm biến, thỉnh thoảng chúng tôi nhận được giá trị 0 giữa các giá trị hợp lệ. Điều này đang tạo ra cho chúng tôi một số vấn đề khi chúng tôi cố gắng sử dụng dữ liệu để chúng tôi muốn lấp đầy khoảng trống 0 với một cái gì đó. Lý tưởng nhất là chúng tôi muốn đặt giá trị trung bình giữa giá trị trước đó và giá trị sau, nếu điều này là không thể, chúng tôi muốn lặp lại giá trị trước đó.

Điều này có thể thực hiện được chỉ với một truy vấn không?

Xin cảm ơn trước.

+0

Có đúng là giả định rằng bạn muốn thực hiện '1' điền 0 với mức trung bình trước khi đăng 2 bản ghi trước khi 2 bản ghi không phải là 0 không? '2'.Nếu không (= nếu có số 0 liên tiếp), hãy điền vào bằng giá trị ghi trước? – bonCodigo

+0

@bonCodigo: đúng. Tất cả các số 0 liên tiếp nên được xử lý như một khoảng trống đặt trên tất cả chúng cùng một giá trị (giá trị trung bình giữa giá trị có sẵn cuối cùng và giá trị sau). Tôi sẽ chỉnh sửa câu hỏi để làm cho nó rõ ràng hơn. –

+0

Phiên bản SQL-Server nào? –

Trả lời

3

Có lẽ không phải là người hiệu quả nhất, nhưng nên làm việc:

WITH cte 
    AS (SELECT [Creation Date], 
       Data, 
       rn=Row_number() OVER(ORDER BY [Creation Date]) 
     FROM dbo.Table) 
UPDATE cte 
SET Data = (((SELECT c2.Data 
        FROM cte c2 
        WHERE c2.rn = cte.rn - 1) 
        + (SELECT c2.Data 
         FROM cte c2 
         WHERE c2.rn = cte.rn + 1))/2) 
WHERE Data = 0; 

tôi sử dụng Row_Number trong một CTE để có được số liên tiếp ra lệnh bởi Creation Date. Sau đó, số này được sử dụng để lấy dữ liệu mới theo giá trị trước đó và tiếp theo của nó.

Here's một bản demo với schema tương tự (tôi đã sử dụng một int thay vì datetime):

Cập nhật

Nice one nhưng nó không xử lý khoảng trống với nhiều 0

Bắt tốt, đây là sql đã sửa đổi tính đến điều đó:

WITH cte 
    AS (SELECT [Creation Date], 
       Data, 
       rn=Row_number() OVER(ORDER BY [Creation Date]) 
     FROM dbo.Table) 
UPDATE cte 
SET Data = (((SELECT c2.Data 
        FROM cte c2 
        WHERE c2.rn = (SELECT MAX(RN)FROM CTE c3 WHERE c3.RN<cte.RN AND c3.Data<>0)) 
        + (SELECT c2.Data 
         FROM cte c2 
         WHERE c2.rn = (SELECT MIN(RN)FROM CTE c3 WHERE c3.RN>cte.RN AND c3.Data<>0)))/2) 
WHERE Data = 0; 

Demo (với số không liên tiếp trên 5,6)

+1

Đẹp nhất nhưng nó không xử lý khoảng cách với nhiều 0 ... –

+0

Ok, nó có thể được sửa đổi để tránh đặt nulls vào 0s trên các vị trí đầu tiên? (với chèn vào giá trị dữ liệu (1,0)) –

+0

Một số khác (quên tôi nếu tôi nhàm chán). Bản cập nhật có thể được sửa đổi để điền số 0 vào các vị trí cuối cùng để bao gồm giá trị cuối cùng có sẵn hay không (chèn vào Giá trị Dữ liệu (10,2555) chèn vào Giá trị Dữ liệu (11,0)). 11 Nên kết thúc với 2555 giá trị –

1

Tôi đã có một biến thể:

SELECT 
BadDate, 
T1.Data AS PrevData, 
T2.Data AS NextData, 
(T1.Data + T2.Data)/2 AS AvgValue 
FROM 

(

SELECT 
T1.CreationDate As BadDate, 
Max(T2.CreationDate) As PrevDate, 
Min(T3.CreationDate) As NextDate 

FROM 
TestData T1, 
TestData T2, 
TestData T3 

WHERE 

T1.Data = 0 
AND T2.Data <> 0 
AND T2.CreationDate < T1.CreationDate 
AND T3.Data <> 0 
AND T3.CreationDate > T1.CreationDate 

GROUP BY T1.CreationDate 

) DateData 

INNER JOIN TestData T1 
ON DateData.PrevDate = T1.CreationDate 

INNER JOIN TestData T2 
ON DateData.NextDate = T2.CreationDate 
+0

Ồ, và tôi đã thử nghiệm nó trên một trang web rất thú vị, SQL Fiddle: http://sqlfiddle.com/#!2/56f28/15 –

+0

Đây là một trong những duy nhất tôi tìm thấy để xử lý tất cả các trường hợp như bắt đầu và kết thúc bằng 0, nhiều 0, v.v. nhưng nó không bao gồm mệnh đề cập nhật. Nó sẽ như thế nào? –

+0

Chỉ cần chọn 'AvgValue' và sử dụng toàn bộ sql làm biểu thức truy vấn phụ trong truy vấn UPDATE của bạn. Xem: http://stackoverflow.com/questions/2586517/subqueries-in-update-set-sql-server-2005 –

0

Nếu bạn không phải lo lắng về việc trung bình, phương pháp này có thể thêm một số để giá trị trước đó.

Cũng xin lưu ý rằng tôi không chắc chắn nếu phương pháp này có bất kỳ vấn đề (trừ cập nhật tất cả các hồ sơ) nhưng thấy chỉ đơn thuần là một cách tiếp cận khác nhau và đơn giản ...

declare @new int = 1 

update mytable 
set @new = val = case when val = 0 then @new + 1 else val end 

Fiddle demo

|   D | VAL | 
--------------------- 
| 2013-01-01 | 123 | 
| 2013-01-02 | 225 | 
| 2013-01-03 | 354 | 
| 2013-01-04 | 578 | 
| 2013-01-05 | 579 |--Updated 
| 2013-01-06 | 2151 | 
| 2013-01-07 | 2331 | 
| 2013-01-08 | 2332 |--Updated 
| 2013-01-09 | 2555 | 
| 2013-01-10 | 2678 | 
+0

Tôi thích có mức trung bình nếu có thể, cảm ơn. –

+0

@SoMoS, chắc chắn đó là sở thích của bạn, nếu bạn nhận được mức tăng trung bình của toàn bộ bộ sưu tập, bạn cũng có thể thêm nó. Điều đó sẽ chính xác hơn là lấy trung bình của hai số gần nhất. – Kaf

0

thêm một lựa chọn

UPDATE s 
SET s.Data = (COALESCE(o1.Data, o2.Data) + COALESCE(o2.Data, o1.Data))/2 
FROM dbo.sensor s OUTER APPLY (
           SELECT TOP 1 s2.Data AS Data 
           FROM dbo.sensor s2 
           WHERE s2.Data != 0 AND s.[Creation Date] < s2.[Creation Date] 
           ORDER BY s2.[Creation Date] ASC       
           ) o1 
        OUTER APPLY (
           SELECT TOP 1 s3.Data AS Data 
           FROM dbo.sensor s3 
           WHERE s3.Data != 0 AND s.[Creation Date] > s3.[Creation Date]        
           ORDER BY s3.[Creation Date] DESC 
           ) o2 
WHERE s.Data = 0  

Demo trên SQLFiddle

+0

Đẹp quá nhưng nó không xử lý những khoảng trống với nhiều 0s quá ... –

+0

@SoMoS Hãy thử câu trả lời mới của tôi;) –

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