2009-12-03 29 views
16

Tôi đã đọc một số bài đăng về vấn đề này nhưng không bao gồm vấn đề này.Làm cách nào để điền vào các "lỗ hổng" trong các trường tự động incremenet?

Tôi đoán là không thể, nhưng tôi vẫn sẽ hỏi.

Tôi có một bảng có hơn 50.000 đăng ký. Đó là một bảng cũ, nơi các hoạt động chèn/xóa khác nhau đã diễn ra.

Điều đó nói rằng, có rất nhiều 'lỗ' khác nhau trong khoảng 300 đăng ký. I.e .: ..., 1340, 1341, 1660, 1661, 1662, ...

Câu hỏi là. Có một cách đơn giản/dễ dàng để làm cho chèn mới điền vào những 'lỗ'?

thx Paulo Bueno

+2

Hãy xem điều này: 'shoes' có id 1,' shirts' có id 2, 'jackets' có id 3. Bạn quyết định xóa' shirts', sau đó bạn quyết định thêm 'pants'. Nếu bạn điền vào khoảng trống, sau đó trong một khoảng thời gian ngắn, các công cụ tìm kiếm sẽ dẫn những người đang tìm kiếm áo sơ mi đến 'example.com/product? Id = 2', đó là ...' pants'. –

Trả lời

9

Lý do bạn cần chức năng này là gì? Db của bạn nên được tốt với những khoảng trống, và nếu bạn đang tiếp cận kích thước tối đa của khóa của bạn, chỉ cần làm cho nó unsigned hoặc thay đổi loại trường.

+0

Cảm ơn Shane, đó chính xác là nỗi lo của tôi. Tôi đang sử dụng mediumint và nó có thể được sử dụng trong một vài năm. Nhưng nếu cần tôi có thể thay đổi nó. –

+0

@PauloBueno Nếu bạn sắp hết dung lượng, chỉ cần cập nhật lược đồ thành loại dữ liệu lớn hơn. * Nhiều, nhiều, nhiều, nhiều, nhiều * giá trị có thể được biểu diễn bằng 64 bit. –

+1

Một câu hỏi khác: điều gì sẽ xảy ra khi đạt đến kích thước tối đa của giá trị gia tăng tự động? Nó sẽ chảy qua và - khi được đặt thành 'unsigned' - bắt đầu lại bằng 0 (hoặc 1)? – johk95

1

Có một cách đơn giản nhưng nó không hoạt động tốt: Chỉ cần cố gắng để chèn với một id và khi thất bại, thử kế tiếp.

Hoặc, chọn ID và khi bạn không nhận được kết quả, hãy sử dụng nó.

Nếu bạn đang tìm cách yêu cầu DB tự động lấp đầy khoảng trống, thì điều đó là không thể. Hơn nữa, nó không bao giờ cần thiết. Nếu bạn cảm thấy bạn cần nó, thì bạn đang lạm dụng một khóa kỹ thuật nội bộ cho một cái gì đó nhưng mục đích duy nhất nó có: Để cho phép bạn tham gia các bảng.

[EDIT] Nếu đây không phải là một khóa chính, sau đó bạn có thể sử dụng báo cáo cập nhật này:

update (
    select * 
    from table 
    order by reg_id -- this makes sure that the order stays the same 
) 
set reg_id = x.nextval 

nơi x là một chuỗi mới mà bạn phải tạo. Điều này sẽ thay đổi tất cả các yếu tố hiện có bảo tồn thứ tự. Điều này sẽ thất bại nếu bạn có các ràng buộc khoá ngoại. Và nó sẽ làm hỏng cơ sở dữ liệu của bạn nếu bạn tham chiếu các ID này ở bất kỳ nơi nào mà không có ràng buộc khóa ngoài.

Lưu ý rằng trong lần chèn tiếp theo, cơ sở dữ liệu sẽ tạo khoảng cách lớn trừ khi bạn đặt lại cột nhận dạng.

4

Bạn thường không cần phải quan tâm đến những khoảng trống. Nếu bạn đang đi đến cuối của datatype cho ID nó nên tương đối dễ dàng để ALTER bảng để nâng cấp lên loại int lớn nhất tiếp theo.

Nếu bạn hoàn toàn phải bắt đầu lấp đầy khoảng trống, đây là một truy vấn để trả lại ID thấp nhất có sẵn (hy vọng không quá chậm):

SELECT MIN(table0.id)+1 AS newid 
FROM table AS table0 
LEFT JOIN table AS table1 ON table1.id=table0.id+1 
WHERE table1.id IS NULL 

(nhớ để sử dụng một giao dịch và/hoặc bắt lặp lại chèn chìa khóa nếu bạn cần chèn đồng thời để hoạt động.)

+0

Sửa đổi nhỏ: nếu bảng trống rỗng truy vấn này sẽ trả về NULL thay vì 1. Sửa lỗi nhanh là sử dụng SELECT IFNULL (MIN (table0.id), 0) + 1 ... – Anton

12

Tôi đồng ý với @Aaron Digulla và @Shane N. Khoảng trống là vô nghĩa. Nếu họ DO có nghĩa là một cái gì đó, đó là một thiết kế cơ sở dữ liệu thiếu sót. Giai đoạn.

Điều đó đang được nói, nếu bạn hoàn toàn CẦN phải lấp đầy các lỗ này, bạn đang chạy ít nhất MySQL 3.23, bạn có thể sử dụng TẠM TẠO để tạo bộ ID mới.Ý tưởng ở đây là bạn sẽ chọn tất cả các ID hiện tại của bạn, theo thứ tự, vào một bảng tạm thời như vậy:

CREATE TEMPORARY TABLE NewIDs 
(
    NewID INT UNSIGNED AUTO INCREMENT, 
    OldID INT UNSIGNED 
) 

INSERT INTO NewIDs (OldId) 
SELECT 
    Id 
FROM 
    OldTable 
ORDER BY 
    Id ASC 

này sẽ cung cấp cho bạn một bảng vẽ bản đồ Id cũ sang một thương hiệu Id mới đó là sẽ được tuần tự trong tự nhiên, do thuộc tính AUTO INCREMENT của cột NewId.

Khi việc này được thực hiện, bạn cần cập nhật bất kỳ tham chiếu nào khác đến Id trong "OldTable" và bất kỳ khóa ngoại nào mà nó sử dụng. Để thực hiện điều này, bạn có thể cần phải DROP bất kỳ ràng buộc khoá ngoại nào mà bạn có, cập nhật bất kỳ tham chiếu nào trong các bảng từ OldId sang NewId, và sau đó tái tạo các ràng buộc khóa ngoài của bạn.

Tuy nhiên, tôi sẽ cho rằng bạn không nên làm BẤT CỨ điều này, và chỉ cần hiểu rằng lĩnh vực Id của bạn tồn tại với mục đích duy nhất của tham khảo một kỷ lục, và nên KHÔNG có bất kỳ sự liên quan cụ thể.

UPDATE: Thêm một ví dụ về việc cập nhật các Id

Ví dụ:

Hãy nói rằng bạn có 2 schemas bảng sau:

CREATE TABLE Parent 
(
    ParentId INT UNSIGNED AUTO INCREMENT, 
    Value INT UNSIGNED, 
    PRIMARY KEY (ParentId) 
) 

CREATE TABLE Child 
(
    ChildId INT UNSIGNED AUTO INCREMENT, 
    ParentId INT UNSIGNED, 
    PRIMARY KEY(ChildId), 
    FOREIGN KEY(ParentId) REFERENCES Parent(ParentId) 
) 

Bây giờ, khoảng cách là xuất hiện trong bảng cha của bạn.

Để cập nhật giá trị của bạn trong mẹ và trẻ em, trước tiên bạn tạo một bảng tạm thời với các ánh xạ:

CREATE TEMPORARY TABLE NewIDs 
(
    Id INT UNSIGNED AUTO INCREMENT, 
    ParentID INT UNSIGNED 
) 

INSERT INTO NewIDs (ParentId) 
SELECT 
    ParentId 
FROM 
    Parent 
ORDER BY 
    ParentId ASC 

Tiếp theo, chúng ta cần phải nói với MySQL để bỏ qua các ràng buộc khoá ngoại vì vậy chúng tôi có thể đúng CẬP NHẬT giá trị của chúng tôi. Chúng tôi sẽ sử dụng cú pháp sau:

SET foreign_key_checks = 0; 

Điều này làm cho MySQL để bỏ qua kiểm tra chính nước ngoài khi cập nhật các giá trị, nhưng nó vẫn sẽ thi hành các loại giá trị chính xác được sử dụng (xem MySQL reference để biết chi tiết).

Tiếp theo, chúng tôi cần cập nhật bảng Cha mẹ và con với các giá trị mới. Chúng tôi sẽ sử dụng tuyên bố UPDATE sau cho điều này:

UPDATE 
    Parent, 
    Child, 
    NewIds 
SET 
    Parent.ParentId = NewIds.Id, 
    Child.ParentId = NewIds.Id 
WHERE 
    Parent.ParentId = NewIds.ParentId AND 
    Child.ParentId = NewIds.ParentId 

Hiện tại, chúng tôi đã cập nhật tất cả các giá trị ParentId chính xác cho các Id được đặt hàng mới từ bảng tạm thời của chúng tôi. Sau khi hoàn tất, chúng ta có thể tái lập nên kiểm tra chính nước ngoài của chúng tôi để duy trì tính toàn vẹn tham chiếu:

SET foreign_key_checks = 1; 

Cuối cùng, chúng tôi sẽ thả bảng tạm thời của chúng tôi để làm sạch nguồn:

DROP TABLE NewIds 

Và đó là .

+0

Tôi không biết bạn có thể làm gì một tham gia trên nhiều bảng trong một UPDATE. Rất tuyệt, cảm ơn! –

-1

Bạn có thể muốn dọn dẹp các khoảng trống trong cột ưu tiên. Cách bên dưới sẽ cung cấp trường tăng tự động cho mức độ ưu tiên. Các thêm trái tham gia vào tabel tương tự sẽ đảm bảo nó được thêm vào theo thứ tự giống như (trong trường hợp này) ưu tiên

SET @a:=0; 
REPLACE INTO footable 
(id,priority) 
    (
    SELECT tbl2.id, @a 
    FROM footable as tbl 
    LEFT JOIN footable as tbl2 ON tbl2.id = tbl.id 
    WHERE (select @a:[email protected]+1) 
    ORDER BY tbl.priority 
) 
2
INSERT INTO prueba(id) 
VALUES (
(SELECT IFNULL(MAX(id) , 0)+1 FROM prueba target)) 

IFNULL cho bỏ qua null trên zero hàng đếm

add mục tiêu cho bỏ qua lỗi mysql "lỗi khoản FROM)

+0

Điều này chỉ chèn một hàng ở cuối bảng với ID của ID tối đa + 1. – Slam

0

Như những người khác đã nói, nó không quan trọng, và nếu nó sau đó một cái gì đó là sai trong thiết kế cơ sở dữ liệu của bạn. Nhưng cá nhân tôi chỉ như họ để được theo thứ tự anyway!

Dưới đây là một số SQL sẽ tạo lại ID của bạn theo cùng thứ tự, nhưng không có khoảng trống.

Nó được thực hiện trước tiên trong trường temp_id (mà bạn sẽ cần phải tạo), vì vậy bạn có thể thấy rằng nó là tất cả tốt trước khi ghi đè ID cũ của bạn. Thay thế Tblid nếu thích hợp.

SELECT @i:=0; 
UPDATE Tbl 
JOIN 
(
    SELECT id 
    FROM Tbl 
    ORDER BY id 
) t2 
ON Tbl.id = t2.id 
SET temp_id = @i:[email protected]+1; 

Bây giờ bạn sẽ có trường temp_id với tất cả ID mới sáng bóng của mình. Bạn có thể đặt chúng trực tiếp chỉ bằng cách:

UPDATE Tbl SET id = temp_id; 

Và sau đó thả cột temp_id.

Tôi phải thừa nhận tôi không hoàn toàn chắc chắn lý do tại sao nó hoạt động, vì tôi đã có thể mong đợi động cơ để khiếu nại về ID trùng lặp, nhưng nó không khi tôi chạy nó.

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