วิธีสร้างดัชนีตามเงื่อนไขใน MySQL


24

จะสร้างดัชนีเพื่อกรองช่วงหรือเซตย่อยของตารางใน MySQL ได้อย่างไร? AFAIK เป็นไปไม่ได้ที่จะสร้างโดยตรง แต่ฉันคิดว่ามันเป็นไปได้ที่จะจำลองคุณลักษณะนี้

ตัวอย่าง: ฉันต้องการสร้างดัชนีสำหรับNAMEคอลัมน์สำหรับแถวด้วยSTATUS = 'ACTIVE'

ฟังก์ชันนี้จะเรียกว่าดัชนีที่กรองแล้วใน SQL Server และดัชนีบางส่วนใน Postgres

คำตอบ:


9

ขณะนี้ MySQL ไม่รองรับดัชนีตามเงื่อนไข

เพื่อให้ได้สิ่งที่คุณถาม (ไม่ใช่ว่าคุณควรทำ;) คุณสามารถเริ่มสร้างตารางเสริม:

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`)
);

จากนั้นคุณเพิ่มทริกเกอร์สามตัวในตารางหลัก:

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 ;

เราต้องการdelimiter //เพราะเราต้องการใช้;ภายในทริกเกอร์

ด้วยวิธีดังกล่าวตารางเสริมจะมีรหัสที่ตรงกับแถวของตารางหลักที่มีสตริง "ACTIVE" ซึ่งได้รับการปรับปรุงโดยทริกเกอร์

หากต้องการใช้สิ่งนั้นบน a selectคุณสามารถใช้สิ่งต่อไปนี้ตามปกติjoin:

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

หากตารางหลักมีข้อมูลอยู่แล้วหรือในกรณีที่คุณทำการดำเนินการภายนอกที่เปลี่ยนแปลงข้อมูลในลักษณะที่ผิดปกติ (EG: นอก MySQL) คุณสามารถแก้ไขตารางเสริมด้วยสิ่งนี้:

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

เกี่ยวกับประสิทธิภาพอาจเป็นไปได้ว่าจะมีการแทรกการอัพเดทและการลบที่ช้า สิ่งนี้สามารถสร้างความรู้สึกได้ก็ต่อเมื่อคุณจัดการกับบางกรณีที่เงื่อนไขที่ต้องการเป็นบวก แม้ว่าจะเป็นเช่นนั้นการทดสอบเพียงอย่างเดียวที่คุณสามารถดูได้ว่าพื้นที่ที่บันทึกไว้นั้นพิสูจน์ความจริงของเรื่องนี้ได้หรือไม่ (และถ้าคุณประหยัดพื้นที่จริงๆ)


7

หากฉันเข้าใจคำถามอย่างถูกต้องฉันคิดว่าสิ่งที่จะทำให้สำเร็จในสิ่งที่คุณพยายามทำคือการสร้างดัชนีทั้งคอลัมน์ชื่อและสถานภาพ ซึ่งจะช่วยให้คุณสามารถค้นหาได้อย่างมีประสิทธิภาพที่ NAME = 'SMITH' และ STATUS = 'ACTIVE'


1
ตกลง แต่นี่ไม่ใช่พื้นที่ว่างที่มีประสิทธิภาพถ้าคุณมีสถานะไม่กี่บรรทัดที่มีสถานะใช้งานอยู่
Maniero

ไม่มันไม่ใช่ แต่นั่นไม่ใช่ข้อกำหนดในคำถามและไม่ได้ระบุว่าตารางถูกถ่วงน้ำหนักอย่างหนักกับค่าใดค่าหนึ่ง เพื่อที่ฉันจะสร้างมุมมองที่เป็นรูปธรรมของสถานะที่คุณกำลังมองหา แต่ MySQL ไม่สนับสนุนสิ่งเหล่านั้น
BlackICE

และพื้นที่ว่างในดิสก์ราคาถูก ...
BlackICE

2
ใช่มันไม่ใช่ข้อกำหนดโดยตรงดังนั้นฉันจึงเริ่มแสดงความคิดเห็นด้วยตกลง ฉันกำลังมองหาทางเลือกมืออาชีพบางอย่าง และทางเลือกมืออาชีพมักจะมองหาวิธีที่มีประสิทธิภาพที่สุดในการทำงานของคุณ คำตอบของคุณอาจเป็นคำตอบที่ชัดเจนที่สุด ไม่มีปัญหากับเรื่องนั้น แต่ฉันไม่เห็นด้วยโดยสิ้นเชิงกับ "พื้นที่ว่างในดิสก์ราคาถูก" ไม่ใช่เพราะราคาแพงแน่นอนว่าราคาถูก แต่หน่วยความจำไม่ถูกดังนั้นหน่วยความจำมีขีด จำกัด ต่ำและดัชนีควรอยู่ในหน่วยความจำอย่างมีประสิทธิภาพเป็นหลัก การเข้าถึงดิสก์ไม่ถูกดังนั้นคำตอบของคุณคือวิธีที่ถูกต้องในการบรรลุเป้าหมาย แต่ฉันสงสัยว่ามันดีที่สุด
Maniero

ฉันจะไม่เห็นด้วยกับหน่วยความจำวันนี้ก็ค่อนข้างถูกเช่นกัน (แน่นอนไม่ถูกเท่าพื้นที่ดิสก์ แต่อยู่ที่ $ 10 / gig สำหรับบางส่วนฉันบอกว่าคุณสามารถ splurge ได้บ้าง :)
BlackICE

6

คุณไม่สามารถทำดัชนีแบบมีเงื่อนไขได้ แต่สำหรับตัวอย่างของคุณคุณสามารถเพิ่มดัชนีหลายคอลัมน์ใน ( name, status)

แม้ว่าจะจัดทำดัชนีข้อมูลทั้งหมดในคอลัมน์เหล่านั้น แต่จะยังช่วยให้คุณค้นหาชื่อที่คุณกำลังมองหาด้วยสถานะ "ใช้งาน"


4

คุณสามารถทำได้โดยแยกข้อมูลระหว่างสองตารางโดยใช้มุมมองเพื่อรวมสองตารางเมื่อต้องการข้อมูลทั้งหมดและการสร้างดัชนีเพียงหนึ่งในตารางในคอลัมน์นั้น - แต่ฉันคิดว่านี่อาจทำให้เกิดปัญหาประสิทธิภาพการทำงานสำหรับแบบสอบถามที่ต้อง เรียกใช้ทั่วทั้งตารางยกเว้นว่าตัววางแผนคิวรีนั้นฉลาดกว่าที่ฉันให้เครดิต โดยพื้นฐานแล้วคุณจะต้องแบ่งพาร์ติชันตารางด้วยตนเอง (และการผนวกดัชนีเข้ากับพาร์ติชันเดียวเท่านั้น)

น่าเสียดายที่ฟีเจอร์การแบ่งพาร์ติชั่นในตัวจะไม่ช่วยคุณในการทำเควสต์เนื่องจากคุณไม่สามารถใช้ดัชนีกับพาร์ติชันเดียว

คุณสามารถรักษาคอลัมน์พิเศษด้วยดัชนีและมีค่าในคอลัมน์นั้นเมื่อเงื่อนไขที่คุณต้องการให้ดัชนีนั้นเป็นจริง แต่สิ่งนี้มีแนวโน้มว่าจะต้องใช้แรงงานมากและมีค่า จำกัด (หรือลบ) ในแง่ของ ประสิทธิภาพการสืบค้นและการประหยัดพื้นที่


ฉันจะไม่มีตารางสองตารางเพียงเพื่อสร้างดัชนีที่ดีกว่าเนื่องจากการเข้าร่วมจะยังคงมีราคาแพงใช่ไหม
jcolebrand

@ jcolebrand: มันจะมีราคาแพงกว่าสำหรับการสืบค้นทั่วไป (ในมุมมองของการรวมกันเป็นสหภาพ) คุณจะต้องเลือกเฉพาะจากตารางพาร์ติชันเพื่อใช้ดัชนี การแบ่งพาร์ติชันในตัวจะทำสิ่งนี้ให้คุณได้อย่างมีประสิทธิภาพ แต่เป็นวิธีที่ Bigown ต้องการ (เพื่อประหยัดพื้นที่) หากสนับสนุนดัชนีเฉพาะพาร์ติชัน ฉันบอกว่าเขาสามารถทำได้ไม่ใช่ว่าเขาต้องการ!
David Spillett

0

ตอนนี้ MySQL มีคอลัมน์เสมือนซึ่งสามารถใช้สำหรับดัชนีได้


3
คุณลักษณะนี้สามารถใช้เพื่อจำลองดัชนีที่กรองได้อย่างไร
ypercubeᵀᴹ

1
@ yper-trollᵀᴹ, druud62 อาจคิดถึง Oracle: dbfiddle.uk/ … - MySQL ไม่เห็นว่าจะใช้ NULLs ในลักษณะเดียวกันแม้ว่า: dbfiddle.uk/
แจ็คดักลาส

@JackDouglas บางที (นั่นไม่ใช่แค่การปรับดัชนีที่ประหยัดพื้นที่โดยวิธีหรือในคำอื่น ๆ สามารถselect count(*) from foo where id is null ;ใช้ดัชนีได้)
ypercubeᵀᴹ

@yper-trollᵀᴹ Oracle ไม่ได้จัดทำดัชนีแถวที่คอลัมน์ดัชนีทั้งหมดเป็น NULL ( use-the-index-luke.com/sql/where-clause/null/index ) - และคอลัมน์เสมือนอาจเป็นdecode(status,'ACTIVE',name,null)เช่นนั้น
แจ็คดักลาส

ขอบคุณฉันคิดว่ามีการเปลี่ยนแปลงในรุ่นล่าสุด (และเป็นโมฆะจัดทำดัชนี)
ypercubeᵀᴹ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.