Làm thế nào để tạo một chỉ mục có điều kiện trong MySQL?


24

Làm cách nào để tạo một chỉ mục để lọc một phạm vi hoặc tập hợp con cụ thể của bảng trong MySQL? AFAIK không thể tạo trực tiếp nhưng tôi nghĩ có thể mô phỏng tính năng này.

Ví dụ: Tôi muốn tạo một chỉ mục cho NAMEcột chỉ cho các hàng vớiSTATUS = 'ACTIVE'

Chức năng này sẽ được gọi là chỉ mục được lọc trong SQL Server và chỉ mục một phần trong Postgres.

Câu trả lời:


9

MySQL hiện không hỗ trợ các chỉ mục có điều kiện.

Để đạt được những gì bạn đang yêu cầu (không phải là bạn nên làm điều đó;)) bạn có thể bắt đầu tạo một bảng phụ:

CREATE TABLE  `my_schema`.`auxiliary_table` (
   `id` int unsigned NOT NULL,
   `name` varchar(250), /* specify the same way as in your main table */
   PRIMARY KEY (`id`),
   KEY `name` (`name`)
);

Sau đó, bạn thêm ba kích hoạt trong bảng chính:

delimiter //

CREATE TRIGGER example_insert AFTER INSERT ON main_table
FOR EACH ROW
BEGIN
   IF NEW.status = 'ACTIVE' THEN
      REPLACE auxiliary_table SET
         auxiliary_table.id = NEW.id,
         auxiliary_table.name = NEW.name;
   END IF;
END;//

CREATE TRIGGER example_update AFTER UPDATE ON main_table
FOR EACH ROW
BEGIN
   IF NEW.status = 'ACTIVE' THEN
      REPLACE auxiliary_table SET
         auxiliary_table.id = NEW.id,
         auxiliary_table.name = NEW.name;
   ELSE
      DELETE FROM auxiliary_table WHERE auxiliary_table.id = OLD.id;
   END IF;
END;//

CREATE TRIGGER example_delete AFTER DELETE ON main_table
FOR EACH ROW
BEGIN
   DELETE FROM auxiliary_table WHERE auxiliary_table.id = OLD.id;
END;//

delimiter ;

Chúng tôi cần delimiter //bởi vì chúng tôi muốn sử dụng ;bên trong các kích hoạt.

Theo cách đó, bảng phụ sẽ chứa chính xác các ID tương ứng với các hàng của bảng chính chứa chuỗi "HOẠT ĐỘNG", được cập nhật bởi các trình kích hoạt.

Để sử dụng trên a select, bạn có thể sử dụng thông thường join:

SELECT main_table.* FROM auxiliary_table LEFT JOIN main_table
   ON auxiliary_table.id = main_table.id
   ORDER BY auxiliary_table.name;

Nếu bảng chính đã chứa dữ liệu hoặc trong trường hợp bạn thực hiện một số thao tác bên ngoài thay đổi dữ liệu theo cách khác thường (EG: bên ngoài MySQL), bạn có thể sửa bảng phụ bằng cách này:

INSERT INTO auxiliary_table SET
   id = main_table.id,
   name = main_table.name,
   WHERE main_table.status="ACTIVE";

Về hiệu suất, có thể bạn sẽ có các phần chèn, cập nhật và xóa chậm hơn. Điều này có thể có ý nghĩa chỉ khi bạn thực sự giải quyết một vài trường hợp trong đó điều kiện mong muốn là tích cực. Ngay cả theo cách đó, có lẽ chỉ kiểm tra bạn có thể xem liệu không gian được lưu có thực sự biện minh cho lỗi này hay không (và nếu bạn thực sự tiết kiệm bất kỳ không gian nào).


7

Nếu tôi hiểu chính xác câu hỏi, tôi nghĩ điều gì sẽ thực hiện những gì bạn đang cố gắng làm là tạo một chỉ mục trên cả hai cột, TÊN và TÌNH TRẠNG. Điều đó sẽ cho phép bạn truy vấn một cách hiệu quả trong đó NAME = 'SMITH' và STATUS = 'ACTIVE'


1
Ok, nhưng đây không phải là không gian hiệu quả nếu bạn có tương đối ít dòng với trạng thái HOẠT ĐỘNG.
Maniero

Không, không phải vậy, nhưng đó không phải là một yêu cầu trong câu hỏi và không nói rõ rằng bảng có trọng số nặng đối với một trong các giá trị. Vì vậy, tôi sẽ tạo ra một cái nhìn cụ thể về TÌNH TRẠNG mà bạn đang tìm kiếm, nhưng MySQL không hỗ trợ những cái đó.
BlackICE

và dung lượng ổ đĩa rẻ ...
BlackICE

2
Vâng, đó không phải là một yêu cầu trực tiếp, vì vậy tôi đã bắt đầu bình luận với một OK. Tôi tìm kiếm một số lựa chọn thay thế chuyên nghiệp. Và các lựa chọn thay thế chuyên nghiệp luôn tìm kiếm cách hiệu quả nhất để thực hiện các nhiệm vụ của bạn. Câu trả lời của bạn có lẽ là câu trả lời rõ ràng nhất. Không vấn đề gì với điều đó. Nhưng tôi hoàn toàn không đồng ý với "dung lượng đĩa là rẻ", không phải vì nó đắt, tất nhiên là rẻ tuy nhiên bộ nhớ không quá rẻ, bộ nhớ có giới hạn thấp và chỉ số nên sống chủ yếu trên bộ nhớ để có hiệu quả. Truy cập đĩa không quá rẻ. Câu trả lời của bạn chắc chắn là một cách chính xác để đạt được mục tiêu nhưng tôi nghi ngờ đó là cách tốt nhất.
Maniero

Tôi cũng không đồng ý với bộ nhớ, nó cũng khá rẻ trong những ngày này (chắc chắn không rẻ như dung lượng ổ đĩa, nhưng với giá 10 đô la / gig cho một số phần, tôi có thể nói bạn có thể phung phí một chút :)
BlackICE

6

Bạn không thể thực hiện lập chỉ mục có điều kiện, nhưng với ví dụ của bạn, bạn có thể thêm chỉ mục nhiều cột trên ( name, status).

Mặc dù nó sẽ lập chỉ mục tất cả dữ liệu trong các cột đó, nhưng nó vẫn sẽ giúp bạn tìm thấy tên bạn đang tìm kiếm với trạng thái "hoạt động".


4

Bạn có thể làm điều này bằng cách chia dữ liệu giữa hai bảng, sử dụng các khung nhìn để hợp nhất hai bảng khi cần tất cả dữ liệu và chỉ lập một trong các bảng trên cột đó - nhưng tôi nghĩ điều này sẽ gây ra vấn đề về hiệu năng cho các truy vấn cần chạy trên toàn bộ bảng trừ khi trình hoạch định truy vấn thông minh hơn tôi cho nó tín dụng. Về cơ bản, bạn sẽ phân vùng bảng theo cách thủ công (và chỉ mục chỉ một trong các phân vùng).

Thật không may tính năng phân vùng bảng tích hợp sẽ không giúp bạn trong nhiệm vụ của mình vì bạn không thể áp dụng một chỉ mục cho một phân vùng duy nhất.

Bạn có thể duy trì một cột bổ sung với một chỉ mục và chỉ có một giá trị trong cột đó khi điều kiện bạn muốn chỉ mục dựa trên là đúng, nhưng điều này có thể sẽ tốn nhiều công sức và có giá trị giới hạn (hoặc âm) hiệu quả truy vấn và tiết kiệm không gian.


Tôi sẽ KHÔNG có hai bảng chỉ để có chỉ mục tốt hơn, vì việc tham gia vẫn sẽ tốn kém phải không?
jcolebrand

@jcolebrand: sẽ tốn kém hơn cho các truy vấn chung (qua các lượt xem thực hiện liên kết), bạn sẽ cần chọn cụ thể từ bảng phân vùng để sử dụng chỉ mục. Phân vùng tích hợp sẽ làm điều này cho bạn một cách hiệu quả, nhưng chỉ theo cách mà Bigown muốn (để tiết kiệm không gian) nếu nó hỗ trợ các chỉ mục cụ thể của phân vùng. Tôi nói anh ta có thể làm điều đó, không phải là anh ta muốn!
David Spillett

0

MySQL hiện có các cột ảo, có thể được sử dụng cho các chỉ mục.


3
Làm thế nào tính năng này có thể được sử dụng để mô phỏng một chỉ mục được lọc?
ypercubeᵀᴹ

1
@ yper-trollᵀᴹ, druud62 có thể nghĩ đến việc Oracle: dbfiddle.uk/... - MySQL không nhìn thấy để NULLs điều trị theo cùng một cách mặc dù: dbfiddle.uk/...
Jack Douglas

@JackDoumund có lẽ. (không phải đó chỉ là một tối ưu hóa chỉ mục giúp tiết kiệm không gian bằng cách nào? Nói cách khác có thể select count(*) from foo where id is null ;sử dụng một chỉ mục không?)
ypercubeᵀᴹ 17/12/17

@ yper-trollᵀᴹ Oracle không lập chỉ mục các hàng trong đó tất cả các cột được lập chỉ mục là NULL ( use-the-index-luke.com/sql/where-clause/null/index ) - và có thể bật một cột ảo decode(status,'ACTIVE',name,null).
Jack Douglas

Thnx, tôi nghĩ rằng đã thay đổi trong các phiên bản gần đây (và null được lập chỉ mục).
ypercubeᵀᴹ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.