2010-06-08 23 views
5

tôi cần mã SQL để giải quyết vấn đề bảng kết hợp được mô tả trên dưới đây:Cần tập trung câu lệnh SQL trên sự kết hợp của bảng nhưng mục luôn luôn với ID duy nhất

Bảng cũ dữ liệu: bảng cũ

name  version status lastupdate  ID 
    A  0.1  on  6/8/2010  1 
    B  0.1  on  6/8/2010  2 
    C  0.1  on  6/8/2010  3 
    D  0.1  on  6/8/2010  4 
    E  0.1  on  6/8/2010  5 
    F  0.1  on  6/8/2010  6 
    G  0.1  on  6/8/2010  7 

bảng dữ liệu mới: bảng mới

name  version status lastupdate  ID   
    A  0.1  on  6/18/2010     
                  #B entry deleted 
    C  0.3  on  6/18/2010    #version_updated 
    C1  0.1  on  6/18/2010    #new_added 
    D  0.1  on  6/18/2010     
    E  0.1  off  6/18/2010    #status_updated 
    F  0.1  on  6/18/2010     
    G  0.1  on  6/18/2010     
    H  0.1  on  6/18/2010    #new_added 
    H1  0.1  on  6/18/2010    #new_added 

sự khác biệt của dữ liệu mới và ngày cũ:

entry B xóa

phiên bản entry C cập nhật

tình trạng nhập E cập nhật

C1/H/H1 tiêu đề bổ sung

Những gì tôi muốn là luôn giữ cho ID mới - mối quan hệ tên ánh xạ trong cũ bảng dữ liệu bất kể dữ liệu đã thay đổi sau đó, hay còn gọi là tên luôn có một số ID duy nhất liên kết với nó.

Nếu mục nhập đã cập nhật, sau đó cập nhật dữ liệu, nếu mục nhập mới được thêm vào, hãy chèn vào bảng, sau đó cung cấp ID duy nhất được gán mới. Nếu mục nhập đã bị xóa, hãy xóa mục nhập và không sử dụng lại ID đó sau.

Tuy nhiên, tôi chỉ có thể sử dụng SQL với câu lệnh chọn hoặc cập nhật đơn giản, vì vậy tôi có thể quá khó để viết mã như vậy, tôi hy vọng ai đó có chuyên môn có thể cung cấp hướng, không có chi tiết cần thiết về biến thể SQL mã sql chuẩn như mẫu là đủ.

Cảm ơn trước!

RGS

KC

======== tôi niêm yết dự thảo sql của tôi ở đây, nhưng không chắc chắn nếu nó hoạt động, một số một với bình luận chuyên môn xin, cảm ơn!

1.duplicate bảng cũ như tmp cho cửa hàng cập nhật

tạo bảng tmp như select * from cũ

2.update vào tmp nơi "tên" là giống nhau trong bảng cũ và mới

cập nhật tmp nơi tên trong (chọn tên từ mới)

3.insert "tên" khác nhau (cũ vs mới) vào tmp và gán ID mới

01.

chèn vào tmp (tên phiên bản status lastupdate ID) đặt idvar = max (chọn tối đa (id) từ tmp) + 1 chọn * từ (chọn new.name new.version new.status new.lastupdate new.ID từ cũ, mới nơi old.name <> new.name)

4. xóa các mục bị xóa khỏi bảng tmp (như B)

xóa từ tmp nơi (chọn ???)

+0

Bạn không có ID trong bảng dữ liệu mới? – tzup

+0

Đầu ra mẫu của bạn không phải là dấu hiệu của những gì bạn mong đợi cho mô tả của bạn. Có phải trường hợp bạn muốn ID vẫn duy trì tuần tự không? – Thomas

+0

Ngoài ra, bạn đang sử dụng sản phẩm và phiên bản cơ sở dữ liệu nào? – Thomas

Trả lời

1

Bạn chưa bao giờ đề cập đến DBMS nào bạn đang sử dụng nhưng nếu bạn đang sử dụng SQL Server, một câu lệnh thực sự tốt là câu lệnh SQL MERGE. Xem: http://www.mssqltips.com/tip.asp?tip=1704

Các lệnh MERGE về cơ bản hoạt động như chèn riêng, cập nhật và xóa báo cáo tất cả trong cùng một tuyên bố . Bạn chỉ định bộ ghi "Nguồn" và bảng "Mục tiêu" và kết hợp giữa hai. Sau đó bạn chỉ định loại sửa đổi dữ liệu sẽ xảy ra khi các bản ghi giữa hai dữ liệu được khớp hoặc không khớp. MERGE rất hữu ích, đặc biệt khi nói đến việc tải bảng kho dữ liệu, có thể là rất lớn và yêu cầu cụ thể hành động cần thực hiện khi hàng hoặc không có mặt.

Ví dụ:

MERGE Products AS TARGET 
USING UpdatedProducts AS SOURCE 
ON (TARGET.ProductID = SOURCE.ProductID) 
--When records are matched, update 
--the records if there is any change 
WHEN MATCHED AND TARGET.ProductName <> SOURCE.ProductName 
OR TARGET.Rate <> SOURCE.Rate THEN 
UPDATE SET TARGET.ProductName = SOURCE.ProductName, 
TARGET.Rate = SOURCE.Rate 
--When no records are matched, insert 
--the incoming records from source 
--table to target table 
WHEN NOT MATCHED BY TARGET THEN 
INSERT (ProductID, ProductName, Rate) 
VALUES (SOURCE.ProductID, SOURCE.ProductName, SOURCE.Rate) 
--When there is a row that exists in target table and 
--same record does not exist in source table 
--then delete this record from target table 
WHEN NOT MATCHED BY SOURCE THEN 
DELETE 
--$action specifies a column of type nvarchar(10) 
--in the OUTPUT clause that returns one of three 
--values for each row: 'INSERT', 'UPDATE', or 'DELETE', 
--according to the action that was performed on that row 
OUTPUT $action, 
DELETED.ProductID AS TargetProductID, 
DELETED.ProductName AS TargetProductName, 
DELETED.Rate AS TargetRate, 
INSERTED.ProductID AS SourceProductID, 
INSERTED.ProductName AS SourceProductName, 
INSERTED.Rate AS SourceRate; 
SELECT @@ROWCOUNT; 
GO 
+0

tôi tin rằng bảng mục tiêu sẽ không có ID. Tôi nghĩ rằng những gì OP có là một bảng (bảng cũ) với ID và dữ liệu liên quan. OP sau đó lấy tập hợp dữ liệu liên quan mới. OP muốn thực hiện những thay đổi này trong bảng gốc bằng cách khớp trên cột tên. ofcourse tôi có thể đọc toàn bộ điều sai :-) – potatopeelings

1

Hãy để tôi bắt đầu từ cuối:

Trong # 4 bạn sẽ xóa tất cả các hàng bằng tmp; những gì bạn muốn nói có WHERE tmp.name NOT IN (SELECT name FROM new); tương tự # 3 không đúng cú pháp, nhưng nếu nó là nó sẽ cố gắng chèn tất cả các hàng.

Về # 2, tại sao không sử dụng auto increment trên ID?

Về số 1, nếu bảng tmp của bạn giống với truy vấn mới # 2- # 4 không có ý nghĩa, trừ khi bạn thay đổi (cập nhật, chèn, xóa) new bảng theo một cách nào đó.

Nhưng (!), Nếu bạn cập nhật bảng new và nó có một lĩnh vực tự động tăng trên ID và nếu bạn đang cập nhật đúng bảng (sử dụng ID) từ các ứng dụng sau đó toàn bộ thủ tục của bạn là không cần thiết (!).

Vì vậy, điều quan trọng là bạn không nên thiết kế hệ thống để hoạt động như trên.

Để có khái niệm cập nhật dữ liệu trong cơ sở dữ liệu từ phía ứng dụng, hãy xem ví dụ here (php/mysql).

Ngoài ra, để có được cú pháp đúng trên truy vấn của bạn, hãy xem qua các phiên bản cơ bản của lệnh SET, INSERT, DELETE và SELECT (không có cách nào xung quanh điều này).

+0

cảm ơn sự chỉnh sửa của bạn, Unreason. Đối với # 2, nếu tôi sử dụng tăng tự động ở # 2, nếu ai đó chèn các mục không mong đợi, nó cũng sẽ nhận được ID cho nó, sẽ rất khó để tìm ra chúng ở đâu sau đó, để chơi an toàn và đơn giản, tôi muốn dữ liệu được xác thực trước khi ID được gán - vì dữ liệu đó được cập nhật/chèn mà không có ID, sau đó sẽ dễ dàng hơn để thêm cơ chế xác minh bằng cách nào đó sau khi tôi có ý tưởng chính về cách chơi hầu hết SQL cho câu chuyện này. –

+0

tôi đồng ý rằng bạn không nên chấp nhận dữ liệu không được mong đợi trong cơ sở dữ liệu của bạn, nhưng một lần nữa, bạn nên kiểm tra dữ liệu trước khi chèn nó. bạn có thể làm điều đó từ ứng dụng (trong mã ứng dụng) hoặc từ cơ sở dữ liệu (kiểm tra và các quy tắc toàn vẹn khác, cộng với kích hoạt); hoặc tốt nhất ở cả hai nơi - ở lớp ứng dụng để thực hiện và tại cơ sở dữ liệu để yên tâm (mặc dù bảo trì nhiều hơn một chút). điều này không liên quan gì đến việc có bảng tạm thời. nó rất, rất có khả năng rằng bảng tạm thời trong trường hợp của bạn không chỉ là thừa, mà còn dẫn đến tất cả các loại phức tạp. – Unreason

+0

ở cuối bạn về cơ bản sẽ đồng bộ hóa hai bảng và có thể rất phức tạp trong nhiều người dùng thực sự với các tình huống xử lý lỗi (đặc biệt là nếu nguyên tử, tính nhất quán, cách ly và độ bền là quan trọng) hoặc rất có thể trong trường hợp của bạn) chỉ đơn giản là không cần thiết. – Unreason

1

Lưu ý - nếu bạn lo lắng về hiệu suất mà bạn có thể bỏ qua câu trả lời này toàn bộ :-)

Nếu bạn có thể thiết kế lại có 2 bảng - một với các dữ liệu và khác với tên - liên kết ID.Một cái gì đó như

table_original

name  version status lastupdate 
A  0.1  on  6/8/2010 
B  0.1  on  6/8/2010 
C  0.1  on  6/8/2010 
D  0.1  on  6/8/2010 
E  0.1  on  6/8/2010 
F  0.1  on  6/8/2010 
G  0.1  on  6/8/2010 

và name_id

name  ID 
A  1 
B  2 
C  3 
D  4 
E  5 
F  6 
G  7 

Khi bạn nhận được table_new với các thiết lập mới của dữ liệu

  1. TRUNCATE table_original
  2. INSERT INTO name_id (tên từ table_new không có trong name_id)
  3. sao chép table_new để table_original

Lưu ý: Tôi nghĩ rằng có một chút mơ hồ về việc xóa đây

Nếu mục đã bị xóa, xóa các entry và không tái sử dụng ID mà sau này.

Nếu tên A bị xóa và lại xuất hiện trong tập hợp cập nhật sau bạn muốn a. sử dụng lại ID gốc được gắn thẻ A hoặc b. tạo ID mới?

Nếu đó là b. bạn cần một cột bị xóa? trong name_id và bước cuối cùng

4. đặt Đã xóa? = Y nơi tên không có trong bảng_original

và 2. sẽ loại trừ Đã xóa? = Bản ghi Y.

Bạn cũng có thể làm điều tương tự mà không có bảng name_id dựa trên logic mà điều duy nhất bạn cần từ table_old là tên - liên kết ID. Mọi thứ khác bạn cần là trong table_new,

+0

cảm ơn, bạn có thể dành thời gian đọc bình luận của tôi dưới đây khi u miễn phí? –

0

một cách tiếp cận dự thảo, tôi không có ý tưởng nếu nó hoạt động tốt ......

CREATE TRIGGER auto_next_id SAU bảng INSERT ON FOR EACH ROW BEGIN bảng CẬP NHẬT SET uid = max (uid) + 1; END;

1

Tính năng này hoạt động trong Informix và cung cấp chính xác màn hình bạn yêu cầu. Tương tự hoặc tương tự nên làm việc trong MySQL, người ta sẽ nghĩ. Bí quyết ở đây là để có được sự kết hợp của tất cả các tên vào một bảng tạm thời và còn lại tham gia vào đó để các giá trị từ hai khác có thể được so sánh.

 
SELECT DISTINCT name FROM old 
UNION 
SELECT DISTINCT name FROM new 
INTO TEMP _tmp; 

SELECT 
    CASE WHEN b.name IS NULL THEN '' 
     ELSE aa.name 
     END AS name, 
    CASE WHEN b.version IS NULL THEN '' 
     WHEN a.version = b.version THEN a.version 
     ELSE b.version 
     END AS version, 
    CASE WHEN a.status = b.status THEN a.status 
     WHEN b.status IS NULL THEN '' 
     ELSE b.status 
     END AS status, 
    CASE WHEN a.lastupdate = b.lastupdate THEN a.lastupdate 
     WHEN b.lastupdate IS NULL THEN null 
     ELSE b.lastupdate 
     END AS lastupdate, 
    CASE WHEN a.name IS NULL THEN '#new_added' 
     WHEN b.name IS NULL THEN '#' || aa.name || ' entry deleted' 
     WHEN a.version b.version THEN '#version_updated' 
     WHEN a.status b.status THEN '#status_updated' 
     ELSE '' 
    END AS change 
    FROM _tmp aa 
    LEFT JOIN old a 
     ON a.name = aa.name 
    LEFT JOIN new b 
     ON b.name = aa.name; 
0

Nếu tôi hiểu rõ những gì bạn cần dựa trên nhận xét trong hai bảng, tôi nghĩ bạn có thể đơn giản hóa rất nhiều vấn đề nếu bạn không hợp nhất hoặc cập nhật bảng cũ vì những gì bạn cần là bảng mới với các ID trong bảng cũ khi chúng tồn tại và các ID mới khi chúng không tồn tại, phải không?

Bản ghi mới: bảng mới có bản ghi mới - OK (nhưng họ cần ID mới) Bản ghi đã xóa: chúng không có trong bảng mới - OK Bản ghi đã cập nhật: đã được cập nhật trong bảng mới - OK (cần ID bản sao từ bảng cũ) Bản ghi chưa sửa đổi: đã có trong bảng mới - OK (cần phải sao chép ID từ bảng cũ)

Vì vậy, điều duy nhất bạn cần làm là: (a) sao chép ID từ bảng cũ để bàn mới khi chúng tồn tại (b) tạo ID mới trong bảng mới khi chúng không tồn tại trong bảng cũ (c) sao chép bảng mới sang bảng cũ.

(a) CẬP NHẬT ID SET mới = IFNULL ((SELECT ID FROM cũ WHERE new.name = old.name), 0);

(b) CẬP NHẬT ID SET mới = FUNCTION_TO GENERATE_ID (new.name) WHERE ID = 0;

(c) Thả bảng cũ; TẠO TABLE cũ (chọn * từ mới);

Vì tôi không biết bạn đang sử dụng cơ sở dữ liệu SQL nào, trong (b) bạn có thể sử dụng hàm sql để tạo id duy nhất tùy thuộc vào cơ sở dữ liệu. Với SQL Server, newid(), Với postgresql (không phải là phiên bản cũ), now() có vẻ là một lựa chọn tốt vì độ chính xác của nó có vẻ đủ (nhưng không phải trong các cơ sở dữ liệu khác như MySQL chẳng hạn như tôi nghĩ độ chính xác chỉ giới hạn trong giây)

Chỉnh sửa: Rất tiếc, tôi không thấy bạn đang sử dụng sqlite và python. Trong trường hợp này, bạn có thể sử dụng hàm str (uuid.uuid4()) (mô đun uuid) trong python để tạo uuid và điền ID trong bảng mới trong đó ID = 0 trong bước (b). Bằng cách này, bạn sẽ có thể tham gia 2 cơ sở dữ liệu độc lập nếu cần mà không có xung đột về ID.

0

Tại sao bạn không sử dụng UUID cho điều này? Tạo nó một lần cho một trình cắm thêm, và kết hợp/giữ nó vào trình cắm thêm, không phải vào trong DB. Bây giờ mà bạn đề cập đến trăn, dưới đây là cách để tạo ra nó:

import uuid 
UID = str(uuid.uuid4()) # this will yield new UUID string 

Chắc chắn nó không đảm bảo tính độc đáo trên toàn cầu, nhưng cơ hội bạn nhận được cùng một chuỗi trong dự án của bạn là khá thấp.