2009-08-28 42 views
18

Tôi có 2 cộtSQL QUERY thay thế giá trị NULL trong một hàng với một giá trị từ giá trị trước đó được biết đến

date number  
---- ------ 
1  3   
2  NULL   
3  5   
4  NULL   
5  NULL   
6  2   
....... 

tôi cần phải thay thế các giá trị NULL với các giá trị mới có trên giá trị từ giá trị cuối cùng được biết đến trong ngày trước đó trong cột ngày ví dụ: ngày = 2 số = 3, ngày 4 và 5 số = 5 và 5. Giá trị NULL xuất hiện ngẫu nhiên.

+2

Bạn có thể vui lòng xác định nhãn hiệu cơ sở dữ liệu SQL bạn đang sử dụng hay không, ví dụ: MySQL, Oracle, SQL-Server và chỉnh sửa câu hỏi của bạn để thêm thẻ đó? –

+0

Bạn có nghĩa là ngày ở hàng trước đó hoặc ngày trước đó (ngày -1) không? Có lẽ đưa ra một ví dụ về dữ liệu mà bạn có trong các cột và một ví dụ về đầu ra mà bạn mong muốn. – Degan

+0

@Bill, anh ta có thể muốn một giải pháp chung chung. Một số người trong chúng ta thực sự * thích * khả năng chuyển đổi dễ dàng giữa DBMS 'khi họ trở thành PITA :-) Tuy nhiên, rất khó (có thể là không thể) để làm điều này trong SQL chuẩn, @mike, vì vậy nếu bạn có một DBMS cụ thể trong tâm trí, bằng mọi cách cho chúng tôi biết. – paxdiablo

Trả lời

2

Trong một cảm giác rất chung chung:

UPDATE MyTable 
SET MyNullValue = MyDate 
WHERE MyNullValue IS NULL 
17

Nếu bạn đang sử dụng SQL Server này nên làm việc

DECLARE @Table TABLE(
     ID INT, 
     Val INT 
) 

INSERT INTO @Table (ID,Val) SELECT 1, 3 
INSERT INTO @Table (ID,Val) SELECT 2, NULL 
INSERT INTO @Table (ID,Val) SELECT 3, 5 
INSERT INTO @Table (ID,Val) SELECT 4, NULL 
INSERT INTO @Table (ID,Val) SELECT 5, NULL 
INSERT INTO @Table (ID,Val) SELECT 6, 2 


SELECT *, 
     ISNULL(Val, (SELECT TOP 1 Val FROM @Table WHERE ID < t.ID AND Val IS NOT NULL ORDER BY ID DESC)) 
FROM @Table t 
0
UPDATE TABLE 
    SET number = (SELECT MAX(t.number) 
        FROM TABLE t 
       WHERE t.number IS NOT NULL 
        AND t.date < date) 
WHERE number IS NULL 
+0

max (t.value) không hoạt động - bạn muốn giá trị có id tối đa SquareCog

+0

@SquareCog: Đọc lại OP: ... thay thế giá trị NULL [number column] bằng new giá trị được lấy từ giá trị [số cột] đã biết cuối cùng trong ngày trước đó trong cột ngày, ví dụ: ngày = 2 số = 3, ngày 4 và 5 số = 5 và 5. –

15

Dưới đây là một giải pháp MySQL:

UPDATE mytable 
SET number = (@n := COALESCE(number, @n)) 
ORDER BY date; 

Đây là súc tích, nhưng sẽ không cần thiết trong các thương hiệu khác của RDBMS. Đối với các thương hiệu khác, có thể có giải pháp dành riêng cho thương hiệu có liên quan hơn. Đó là lý do tại sao điều quan trọng là phải cho chúng tôi biết thương hiệu bạn đang sử dụng.

Thật tuyệt khi được nhà cung cấp độc lập, như @Pax đã nhận xét, nhưng thất bại, bạn cũng nên sử dụng thương hiệu cơ sở dữ liệu đã chọn của mình để tận dụng tối đa lợi thế của nó.


Giải thích về các truy vấn trên:

@n là một biến sử dụng MySQL. Nó bắt đầu ra NULL, và được gán một giá trị trên mỗi hàng khi UPDATE chạy qua các hàng. Trong đó number không phải là NULL, @n được gán giá trị là number. Trường hợp number là NULL, thì COALESCE() mặc định là giá trị trước đó là @n. Trong cả hai trường hợp, giá trị này sẽ trở thành giá trị mới của cột number và UPDATE tiếp tục đến hàng tiếp theo. Biến số @n vẫn giữ giá trị của nó từ hàng này sang hàng khác, do đó các hàng tiếp theo nhận được các giá trị đến từ (các) hàng trước đó. Thứ tự của UPDATE có thể dự đoán được, vì việc sử dụng đặc biệt của ORDER BY với UPDATE của MySQL (đây không phải là SQL chuẩn).

+1

COALESCE được hỗ trợ trên SQL Server (2000?), và Oracle 9i +, nhưng tôi không hiểu những gì @n đang làm. –

+0

@rexem: Xem chỉnh sửa ở trên. –

6

Đây là giải pháp Oracle (10g hoặc cao hơn).

SQL> select * 
    2 from mytable 
    3 order by id 
    4/

     ID SOMECOL 
---------- ---------- 
     1   3 
     2 
     3   5 
     4 
     5 
     6   2 

6 rows selected. 

SQL> select id 
    2   , last_value(somecol ignore nulls) over (order by id) somecol 
    3 from mytable 
    4/

     ID SOMECOL 
---------- ---------- 
     1   3 
     2   3 
     3   5 
     4   5 
     5   5 
     6   2 

6 rows selected. 

SQL> 
+0

Bạn có thể sử dụng LAG thay vì LAST_VALUE không? Nếu vậy, điều đó sẽ làm cho nó tương thích 8i +. –

+1

Không. LAG() chỉ hoạt động với độ lệch cố định. Dữ liệu thử nghiệm đã cho có độ lệch thay đổi. – APC

+1

Tôi đã đi một chặng đường dài để tìm câu trả lời này! IGNORE NULLS đã làm tất cả các phép thuật! – vdolez

1

Trước hết, bạn có thực sự cần lưu trữ các giá trị không? Bạn chỉ có thể sử dụng quan điểm cho rằng không được công việc:

SELECT t."date", 
     x."number" AS "number" 
FROM @Table t 
JOIN @Table x 
    ON x."date" = (SELECT TOP 1 z."date" 
        FROM @Table z 
        WHERE z."date" <= t."date" 
         AND z."number" IS NOT NULL 
        ORDER BY z."date" DESC) 

Nếu bạn thực sự không có cột ID ("date") và nó là một khóa chính (nhóm), sau đó truy vấn này nên được khá nhanh. Nhưng hãy kiểm tra gói truy vấn: có thể tốt hơn nếu bạn có chỉ mục bao gồm cột Val.

Ngoài ra nếu bạn không thích các thủ tục khi bạn có thể tránh chúng, bạn cũng có thể sử dụng truy vấn tương tự cho UPDATE:

UPDATE t 
SET  t."number" = x."number" 
FROM @Table t 
JOIN @Table x 
    ON x."date" = (SELECT TOP 1 z."date" 
        FROM @Table z 
        WHERE z."date" < t."date" --//@note: < and not <= here, as = not required 
         AND z."number" IS NOT NULL 
        ORDER BY z."date" DESC) 
WHERE t."number" IS NULL 

Chú ý: mã phải công trình trên "SQL Server".

8

Giải pháp tốt nhất là giải pháp được cung cấp bởi Bill Karwin.Gần đây tôi đã phải giải quyết điều này trong một kết quả tương đối lớn (1000 hàng với 12 cột mỗi loại cần "hiển thị giá trị không null cuối cùng nếu giá trị này là null trên hàng hiện tại") và sử dụng phương thức cập nhật với đầu 1 chọn cho giá trị đã biết trước đó (hoặc truy vấn phụ có đầu trang 1) chạy siêu chậm.

Tôi đang sử dụng SQL 2005 và cú pháp cho một sự thay thế biến là hơi khác so với mysql:

UPDATE mytable 
SET 
    @n = COALESCE(number, @n), 
    number = COALESCE(number, @n) 
ORDER BY date 

Tuyên bố tập đầu tiên cập nhật các giá trị của @n biến để giá trị hàng hiện tại của 'số' nếu 'số' không phải là null (COALESCE trả về đối số không null đầu tiên bạn chuyển vào nó) Câu lệnh set thứ hai cập nhật giá trị cột thực tế cho 'number' thành chính nó (nếu không null) hoặc biến @n (luôn luôn chứa giá trị không NULL cuối cùng gặp phải).

Vẻ đẹp của cách tiếp cận này là không có tài nguyên bổ sung nào được sử dụng để quét bảng tạm thời lặp đi lặp lại ... Bản cập nhật trong hàng @n sẽ theo dõi giá trị không null cuối cùng.

Tôi không có đủ đại diện để bỏ phiếu cho câu trả lời của anh ấy, nhưng ai đó cần. Đó là màn trình diễn thanh lịch và hay nhất.

+0

SQLite không hỗ trợ biến. Tuy nhiên, nếu các mục nhập có một id autoincrement và đã được chèn với ngày như là một chức năng đơn điệu, sau đó mỗi hàng liên tiếp có thể tham khảo trước đó. Một trường hợp rất cụ thể, mặc dù. CẬP NHẬT số SET của chúng tôi = COALESCE (số, (SELECT t.number TỪ mytable t WHERE mytable.id = t.id + 1)); – Sussch

+0

SQL 2012 không hỗ trợ Đơn đặt hàng trước. Tôi phải đi với câu trả lời của Adriaan Stander. –

4

Tôi biết nó là một diễn đàn rất cũ, nhưng tôi đã gặp vấn đề này trong khi khắc phục sự cố của tôi :) chỉ nhận ra rằng những kẻ khác đã đưa ra một giải pháp phức tạp cho vấn đề trên. Xin xem giải pháp của tôi dưới đây:

DECLARE @A TABLE(ID INT, Val INT) 

INSERT INTO @A(ID,Val) SELECT 1, 3 
INSERT INTO @A(ID,Val) SELECT 2, NULL 
INSERT INTO @A(ID,Val) SELECT 3, 5 
INSERT INTO @A(ID,Val) SELECT 4, NULL 
INSERT INTO @A(ID,Val) SELECT 5, NULL 
INSERT INTO @A(ID,Val) SELECT 6, 2 

UPDATE D 
    SET D.VAL = E.VAL 
    FROM (SELECT A.ID C_ID, MAX(B.ID) P_ID 
      FROM @A AS A 
      JOIN @A AS B ON A.ID > B.ID 
      WHERE A.Val IS NULL 
      AND B.Val IS NOT NULL 
      GROUP BY A.ID) AS C 
    JOIN @A AS D ON C.C_ID = D.ID 
    JOIN @A AS E ON C.P_ID = E.ID 

SELECT * FROM @A 

Hy vọng điều này có thể giúp một ai đó :)

+0

A belated +1 theo cách tiếp cận đáng yêu nhưng tuyệt vời của bạn. – pilcrow

1

Đây là giải pháp cho MS Access.

Bảng ví dụ được gọi là tab, với các trường idval.

SELECT (SELECT last(val) 
      FROM tab AS temp 
      WHERE tab.id >= temp.id AND temp.val IS NOT NULL) AS val2, * 
    FROM tab; 
5

Tập lệnh sau giải quyết vấn đề này và chỉ sử dụng SQL ANSI đơn giản. Tôi đã thử nghiệm giải pháp này trên SQL2008, SQLite3Oracle11g.

CREATE TABLE test(mysequence INT, mynumber INT); 

INSERT INTO test VALUES(1, 3); 
INSERT INTO test VALUES(2, NULL); 
INSERT INTO test VALUES(3, 5); 
INSERT INTO test VALUES(4, NULL); 
INSERT INTO test VALUES(5, NULL); 
INSERT INTO test VALUES(6, 2); 

SELECT t1.mysequence, t1.mynumber AS ORIGINAL 
, (
    SELECT t2.mynumber 
    FROM test t2 
    WHERE t2.mysequence = (
     SELECT MAX(t3.mysequence) 
     FROM test t3 
     WHERE t3.mysequence <= t1.mysequence 
     AND mynumber IS NOT NULL 
     ) 
) AS CALCULATED 
FROM test t1; 
0

Nếu bạn đang tìm kiếm một giải pháp cho chuyển đỏ, điều này sẽ làm việc với các khoản khung:

ngày SELECT, last_value (ColumnName bỏ qua null) trên (thứ tự theo ngày hàng giữa vô biên trước và hàng hiện tại) như ColumnName từ tbl

-3

Hãy thử điều này:

update Projects 
set KickOffStatus=2 
where KickOffStatus is null 
Các vấn đề liên quan