PostgreSQL สามารถใช้ null ในดัชนีได้หรือไม่


10

ฉันอ่านหนังสือเล่มนี้ซึ่งบอกว่า

ฐานข้อมูลสันนิษฐานว่า Indexed_Col IS NOT NULL ครอบคลุมช่วงที่มีขนาดใหญ่เกินไปที่จะเป็นประโยชน์ดังนั้นฐานข้อมูลจะไม่ขับไปยังดัชนีจากเงื่อนไขนี้

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

นอกจากนี้ในการทำงานEXPLAIN ANALYZEในSELECTแบบสอบถามที่ฉันได้พบว่าไม่มีการจัดทำดัชนีของฉันจะถูกนำมาใช้แม้ในขณะที่สิทธิทั้งหมดที่พวกเขาควรจะเป็น

ดังนั้นคำถามของฉันคือ:

สมมติว่ามีตารางที่มีคอลัมน์ซึ่งคำจำกัดความของคอลัมน์รวมถึง "NOT NULL" และดัชนีที่มีอยู่ซึ่งครอบคลุมคอลัมน์นี้ดัชนีนี้จะถูกใช้ในแบบสอบถามของตารางนั้นซึ่งคอลัมน์เป็นส่วนหนึ่งของแบบสอบถามหรือไม่

ชอบ:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;

คำตอบ:


9

PostgreSQL แน่นอนสามารถIS NOT NULLใช้ดัชนีสำหรับ ฉันไม่เห็นข้อสรุปของผู้วางแผนแบบสอบถามเกี่ยวกับเงื่อนไขนั้น

หากเศษส่วนที่เป็นโมฆะสำหรับคอลัมน์ ( pg_statistic.stanullfrac) ต่ำพอที่จะแนะนำให้ดัชนีเลือกใช้แบบสอบถามอย่างเป็นประโยชน์ PostgreSQL จะใช้ดัชนี

ฉันไม่สามารถเข้าใจสิ่งที่คุณพยายามจะพูดด้วย:

หากสิ่งนี้ถูกต้องฉันเข้าใจหรือไม่ว่าดัชนีในคอลัมน์ที่กำหนดเป็น "ไม่เป็นโมฆะ" จะไม่ถูกใช้ในแบบสอบถามที่ใช้คอลัมน์นั้นหรือไม่

แน่นอนว่าดัชนีจะไม่ถูกใช้สำหรับIS NOT NULLเงื่อนไขในNOT NULLคอลัมน์ มันจะจับคู่กับ 100% ของแถวเสมอดังนั้น seqscan จะเร็วกว่ามาก

PostgreSQL จะไม่ใช้ดัชนีหากดัชนีไม่กรองแถวจำนวนมากสำหรับการสืบค้น ข้อยกเว้นที่เป็นไปได้เพียงอย่างเดียวคือเมื่อคุณขอชุดของคอลัมน์ที่ครอบคลุมโดยดัชนีเดียวตามลำดับที่ตรงกับของดัชนี PostgreSQL อาจทำการสแกนดัชนีเท่านั้น เช่นถ้ามีดัชนีt(a, b, c)และคุณ:

select a, b FROM t ORDER BY a, b, c;

PostgreSQL อาจใช้ดัชนีของคุณแม้ว่าจะไม่มีการกรองแถวออกเพราะเพียงอ่านดัชนีและสามารถข้ามอ่าน heap ได้หลีกเลี่ยงการเรียงลำดับ ฯลฯ


ทั้งหมดนี้เป็นจริงตั้งแต่วันที่ PG 9.0
eradman

1
และแม้แต่ในคอลัมน์ที่มีค่าว่างแบบสอบถามที่มีเงื่อนไขWHERE column IS NOT NULLอาจไม่ใช้ดัชนีเพราะตามที่หนังสือบอกว่า: "ครอบคลุมช่วงที่มีขนาดใหญ่เกินไปที่จะเป็นประโยชน์" หาก 90% ของค่าไม่เป็นโมฆะ seqscan ก็อาจเร็วขึ้นเช่นกัน
ypercubeᵀᴹ

เผง มันอาจ แต่เฉพาะในกรณีที่เศษส่วนขนาดใหญ่ของตารางเป็นโมฆะ บ่อยครั้งในกรณีนี้ดัชนีบางส่วนเป็นทางเลือกที่ดีกว่าอยู่ดี
Craig Ringer

ใช่. ฉันพยายามที่จะบอกว่า (ตามที่ฉันเข้าใจ) ส่วน"ครอบคลุมช่วงที่มีขนาดใหญ่เกินไป"หมายถึงดัชนี แต่เกี่ยวกับเงื่อนไขที่เฉพาะเจาะจงและไม่ใช่ดัชนีโดยทั่วไป
ypercubeᵀᴹ

2
@FuelFolder Heh มีการปฏิเสธมากเกินไปที่นี่ PostgreSQL จะไม่ใช้ดัชนีในNOT NULLคอลัมน์สำหรับIS NOT NULLแบบสอบถามยกเว้นว่าดัชนีนั้นจะมีประโยชน์สำหรับส่วนอื่น ๆ ของส่วนWHEREคำสั่งเข้าร่วมตัวกรอง ฯลฯ หรือใช้สำหรับการสแกนดัชนีเท่านั้นที่สั่ง กล่าวอีกนัยหนึ่งมันจะเพิกเฉยต่อความซ้ำซ้อนIS NOT NULLในNOT NULLคอลัมน์และเลือกตัวเลือกการใช้ดัชนีตามรายละเอียดอื่น ๆ (ดูการแก้ไขและสแกนเฉพาะดัชนีเท่านั้น)
Craig Ringer

2

นอกจากคำตอบอย่างละเอียดของ Craig ฉันต้องการเพิ่มว่าปกหนังสือที่คุณอ้างอิงบอกว่า:

ครอบคลุม Oracle, DB2 และ SQL Server

ดังนั้นฉันจะไม่เชื่อว่ามันจะเป็นคำแนะนำที่ดีเยี่ยมสำหรับ PostgreSQL โดยเฉพาะ ทุก RDBMS สามารถแตกต่างกันอย่างน่าประหลาดใจ!

ฉันสับสนเล็กน้อยเกี่ยวกับคำถามเดิมของคุณ แต่นี่เป็นตัวอย่างที่แสดงว่าส่วนของหนังสือไม่ถูกต้อง 100% เพื่อหลีกเลี่ยงความสับสนต่อไปนี้เป็นวรรคที่เกี่ยวข้องทั้งหมดคุณสามารถดูได้ในGoogle Book Search

ฐานข้อมูลสันนิษฐานว่า Indexed_Col IS NOT NULL ครอบคลุมช่วงที่มีขนาดใหญ่เกินไปที่จะเป็นประโยชน์ดังนั้นฐานข้อมูลจะไม่ขับไปยังดัชนีจากเงื่อนไขนี้ ในบางกรณีการมีค่าที่ไม่เป็นโมฆะนั้นหาได้ยากซึ่งการสแกนช่วงดัชนีสำหรับค่าที่ไม่เป็นไปได้ทั้งหมดจะเป็นประโยชน์ ในกรณีเช่นนี้หากคุณสามารถหาขีด จำกัด ล่างหรือบนที่ปลอดภัยถึงช่วงของค่าที่เป็นไปได้ทั้งหมดคุณสามารถเปิดใช้งานการสแกนแบบช่วงด้วยเงื่อนไขเช่น Positive_ID_Column> -1 หรือ Date_Column> TO_DATE ('0001/01/01' , 'YYYY / MM / DD')

Postgres สามารถจริง (ในกรณีที่วางแผนไว้ดังต่อไปนี้) ใช้ดัชนีเพื่อตอบสนองIS NOT NULLคำสั่งโดยไม่ต้องเพิ่มช่วง kludges Positive_ID_Column > -1สแกนเช่นปัญหา ดูความคิดเห็นเกี่ยวกับคำถามของ Craig เกี่ยวกับสาเหตุที่ Postgres เลือกดัชนีนี้ในกรณีนี้โดยเฉพาะและหมายเหตุเกี่ยวกับการใช้ดัชนีบางส่วน

CREATE TABLE bar (a int);
INSERT INTO bar (a) SELECT NULL FROM generate_series(1,1000000);
INSERT INTO bar (a) VALUES (1);
CREATE INDEX bar_idx ON bar (a);

EXPLAIN ANALYZE SELECT * FROM bar WHERE a IS NOT NULL;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Only Scan using bar_idx on bar  (cost=0.42..8.44 rows=1 width=4) (actual time=0.094..0.095 rows=1 loops=1)
   Index Cond: (a IS NOT NULL)
   Heap Fetches: 1
 Total runtime: 0.126 ms
(4 rows)

นี่คือ Postgres 9.3 แต่ฉันเชื่อว่าผลลัพธ์จะคล้ายกันในข้อ 9.1 แม้ว่าจะไม่ใช้ "การสแกนดัชนีเท่านั้น"

แก้ไข: ฉันเห็นคุณได้ชี้แจงคำถามเดิมของคุณแล้วและคุณสงสัยว่าทำไม Postgres จึงไม่ใช้ดัชนีในตัวอย่างง่ายๆเช่น:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;

อาจเป็นเพราะคุณไม่มีแถวในตาราง ANALYZE my_table;ดังนั้นการเพิ่มข้อมูลการทดสอบบางอย่างและ


ในคำอธิบายของหนังสือเล่มนี้ (เน้นที่เหมือง): "ผู้เขียน Dan Tow แสดงวิธีการประหยัดเวลาที่เขาพัฒนาขึ้นเพื่อค้นหาแผนการดำเนินการที่เหมาะสม - อย่างรวดเร็วและเป็นระบบ - โดยไม่คำนึงถึงความซับซ้อนของ SQL หรือแพลตฟอร์มฐานข้อมูลที่ใช้ " บางทีคุณอาจมองข้าม # 1 ของคำถามกล่าวคือคอลัมน์ถูกกำหนดเป็นNOT NULLไม่ใช่แบบสอบถามใช้IS NOT NULLเป็นเงื่อนไขดัชนี นี่คือความคิดเห็นที่คุณอ้างอิง แต่ฉันจะอัปเดตคำถามเพื่อรวมไว้
FuriousFolder

นอกจากนี้หนังสือเล่มนี้เป็นผู้ไม่เชื่อเรื่องภาษา: ส่วนที่เฉพาะเจาะจงของ DMBS นั้นเกี่ยวกับการแสดงแผนการสืบค้นซึ่ง Postgres ทำให้ค่อนข้างง่าย :)
FuriousFolder

1
@FULLFolder คอลัมน์ถูกกำหนดเป็น NOT NULL แต่ในส่วนนี้ (ในคำถามของคุณจากหนังสือ): "ที่ Indexed_Col IS NOT NULL ครอบคลุม ... "หมายถึงเงื่อนไขของสถานที่และไม่ใช่นิยามคอลัมน์ แม้ว่ามันจะยากที่จะแน่ใจเพราะมันไม่อยู่ในบริบท บางทีคุณควรรวมย่อหน้าทั้งหมด (ก่อนหน้า) จากหนังสือ
ypercubeᵀᴹ

-1

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

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

แต่มากกว่าสองสามแถวมันถูกกว่าที่จะข้ามสมุดโทรศัพท์และวนซ้ำแถวทั้งหมดในตารางหลัก จากประสบการณ์ของฉันจุดเปลี่ยนอยู่ที่ประมาณ 100 แถว


"ดัชนีเป็นเหมือนสมุดโทรศัพท์ที่แปลคอลัมน์เป็นที่ตั้งของแถวหากคุณกำลังมองหาเพียงไม่กี่แถวมันจะเป็นการเหมาะสมที่จะค้นหาแต่ละแถวในสมุดโทรศัพท์จากนั้นค้นหาแถวในตารางหลัก" ที่จริงแล้วดัชนีเป็นเหมือนสมุดโทรศัพท์ขนาดเล็กซึ่งมีการปรับปรุงทุกครั้งที่สมุดโทรศัพท์ที่ดัชนีถูกอัพเดต คุณรู้ไหมว่าเมื่อใดก็ตามที่คุณเปิดสมุดโทรศัพท์ที่เล็กลงคุณจะพบข้อมูลใด ๆ และทั้งหมดที่เงื่อนไขการทำดัชนีอธิบายไว้ คนเช่นทุกชื่อว่า CREATE INDEX ix_frank ON people(name) WHERE name ='frank''ตรงไปตรงมาบนโต๊ะดัชนี:
FuriousFolder

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

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

อันโดมาร์ ... ฉันคือโอพีฮ่าฮ่า เป้าหมายของฉันคือตรงนั้น; เพื่อให้ได้แบบสอบถามนี้เพื่อใช้การสแกนแบบดัชนีเท่านั้น ฉันได้ประสบความสำเร็จตั้งแต่มันตั้งแต่เครกอธิบายว่า Postgres คือสามารถที่จะใช้ดัชนีในคอลัมน์ที่นิยามของคอลัมน์รวมถึง NOT NULL
FuriousFolder
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.