ฉันจะรับ "แถวที่สอดคล้องกันล่าสุด" อย่างมีประสิทธิภาพได้อย่างไร


53

ฉันมีรูปแบบแบบสอบถามที่ต้องพบบ่อยมาก แต่ฉันไม่รู้วิธีเขียนแบบสอบถามที่มีประสิทธิภาพ ฉันต้องการค้นหาแถวของตารางที่ตรงกับ "วันที่ล่าสุดไม่หลัง" แถวของตารางอื่น

ฉันมีตารางinventoryพูดซึ่งแสดงถึงสินค้าคงคลังที่ฉันถือในวันหนึ่ง

date       | good | quantity
------------------------------
2013-08-09 | egg  | 5
2013-08-09 | pear | 7
2013-08-02 | egg  | 1
2013-08-02 | pear | 2

และโต๊ะ "ราคา" พูดซึ่งถือราคาสินค้าในวันที่กำหนด

date       | good | price
--------------------------
2013-08-07 | egg  | 120
2013-08-06 | pear | 200
2013-08-01 | egg  | 110
2013-07-30 | pear | 220

ฉันจะรับราคา "ล่าสุด" อย่างมีประสิทธิภาพสำหรับแต่ละแถวของตารางคลังโฆษณาได้อย่างไรเช่น

date       | pricing date | good | quantity | price
----------------------------------------------------
2013-08-09 | 2013-08-07   | egg  | 5        | 120
2013-08-09 | 2013-08-06   | pear | 7        | 200
2013-08-02 | 2013-08-01   | egg  | 1        | 110
2013-08-02 | 2013-07-30   | pear | 2        | 220

ฉันรู้วิธีหนึ่งในการทำสิ่งนี้:

select inventory.date, max(price.date) as pricing_date, good
from inventory, price
where inventory.date >= price.date
and inventory.good = price.good
group by inventory.date, good

จากนั้นเข้าร่วมแบบสอบถามนี้อีกครั้งเพื่อสินค้าคงคลัง สำหรับตารางขนาดใหญ่ที่ทำแบบสอบถามแรก (โดยไม่ต้องเข้าร่วมอีกครั้งกับสินค้าคงคลัง) ช้ามาก อย่างไรก็ตามปัญหาเดียวกันนี้ได้รับการแก้ไขอย่างรวดเร็วหากฉันใช้ภาษาการเขียนโปรแกรมเพื่อออกmax(price.date) ... where price.date <= date_of_interest ... order by price.date desc limit 1แบบสอบถามหนึ่งรายการสำหรับแต่ละรายการdate_of_interestจากตารางคลังโฆษณาดังนั้นฉันจึงรู้ว่าไม่มีอุปสรรคในการคำนวณ อย่างไรก็ตามฉันต้องการแก้ไขปัญหาทั้งหมดด้วยแบบสอบถาม SQL เดียวเพราะจะทำให้ฉันสามารถดำเนินการประมวลผล SQL เพิ่มเติมจากผลลัพธ์ของแบบสอบถามได้

มีวิธีมาตรฐานในการทำสิ่งนี้อย่างมีประสิทธิภาพหรือไม่? รู้สึกเหมือนต้องมาบ่อย ๆ และควรมีวิธีเขียนแบบสอบถามที่รวดเร็ว

ฉันใช้ Postgres แต่คำตอบทั่วไปของ SQL จะได้รับการชื่นชม


3
โหวตให้ย้ายไปยัง DBA.SE เนื่องจากเป็นคำถามที่มีประสิทธิภาพ เราสามารถเขียนแบบสอบถามด้วยวิธีที่ต่างกันเล็กน้อย แต่นั่นจะไม่ทำให้เร็วขึ้น
ypercubeᵀᴹ

5
คุณต้องการสินค้าทั้งหมดตลอดทั้งวันจากการสืบค้นเพียงครั้งเดียวหรือไม่? ดูเหมือนความต้องการที่ไม่น่าจะเป็นไปอย่างนั้นเหรอ? โดยทั่วไปจะเรียกราคาสำหรับวันที่ระบุหรือราคาสำหรับสินค้าเฉพาะ (ณ วันที่ระบุ) ข้อความค้นหาทางเลือกเหล่านั้นสามารถได้รับประโยชน์จากดัชนี (เหมาะสม) ได้ง่ายขึ้น เราจำเป็นต้องรู้ด้วยเช่นกันว่าcardinalities (มีกี่แถวในแต่ละตาราง?) รวมถึงคำจำกัดความของตารางที่สมบูรณ์ ชนิดข้อมูล, ข้อ จำกัด , ดัชนี, ... (ใช้\d tblใน psql), เวอร์ชันของ Postgresและ min / สูงสุด จำนวนราคาต่อดี
Erwin Brandstetter

@ErwinBrandstetter คุณกำลังขอให้ฉันตอบคำถามหรือไม่? ฉันไม่มีคุณสมบัติที่จะรู้ว่าสิ่งไหนดีที่สุด แต่ในขณะที่คุณมี upvotes มากที่สุดฉันยินดีที่จะยอมรับมัน
Tom Ellis

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

1
ฉันต้องขอโทษด้วยเพราะถึงแม้ว่าฉันจะได้รับสิ่งที่ดูเหมือนจะเป็นคำตอบที่ยอดเยี่ยมฉันไม่ได้ทำงานกับปัญหาที่กระตุ้นคำถามดังนั้นฉันจึงไม่สามารถตัดสินได้ว่าเป็นคำตอบที่ดีที่สุดหรือหากพวกเขาจริง ๆ เหมาะมากสำหรับกรณีการใช้งานของฉัน (เหมือนเดิม) หากมี DBA.Stackexchange ettiquette บางอย่างฉันควรทำตามในกรณีนี้โปรดแจ้งให้เราทราบ
Tom Ellis

คำตอบ:


42

มันขึ้นอยู่กับสถานการณ์และข้อกำหนดที่แน่นอน พิจารณาความคิดเห็นของฉันกับคำถาม

ทางออกที่ง่าย

ด้วยDISTINCT ONใน Postgres:

SELECT DISTINCT ON (i.good, i.the_date)
       i.the_date, p.the_date AS pricing_date, i.good, p.price
FROM   inventory  i
LEFT   JOIN price p ON i.good = p.good AND i.the_date >= p.the_date
ORDER  BY i.good, i.the_date, p.the_date DESC;

ผลการสั่งซื้อ

หรือด้วยNOT EXISTSมาตรฐาน SQL (ใช้ได้กับ RDBMS ทุกตัวที่ฉันรู้จัก):

SELECT i.the_date, p.the_date AS pricing_date, i.good, i.quantity, p.price
FROM   inventory  i
LEFT   JOIN price p ON p.good = i.good AND p.the_date <= i.the_date
WHERE  NOT EXISTS (
   SELECT 1 FROM price p1
   WHERE  p1.good = p.good
   AND p1.the_date <= i.the_date
   AND p1.the_date >  p.the_date
   );

ผลเดียวกัน แต่มีการเรียงลำดับโดยพลการ - ORDER BYถ้าคุณเพิ่ม
ข้อกำหนดและดัชนีที่แน่นอนหนึ่งในนั้นอาจเร็วกว่า
โดยทั่วไปแล้วDISTINCT ONจะเป็นผู้ชนะและคุณจะได้รับผลลัพธ์ที่เรียงลำดับอยู่ด้านบน แต่สำหรับบางกรณีเทคนิคการสืบค้นอื่น ๆ นั้นเร็วกว่ามาก ดูด้านล่าง

โซลูชันที่มีคิวรีย่อยเพื่อคำนวณค่าสูงสุด / นาทีโดยทั่วไปจะช้ากว่า โดยทั่วไปแล้วตัวแปรที่มี CTE จะช้ากว่า

มุมมองธรรมดา (เช่นเสนอโดยคำตอบอื่น) ไม่ได้ช่วยประสิทธิภาพเลยใน Postgres

ซอ Fiddle


ทางออกที่เหมาะสม

เงื่อนไขและการเรียง

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

การเรียงลำดับตามประเภทตัวอักษร ( text,, varchar... )จะต้องดำเนินการตามภาษา - โดยเฉพาะอย่างยิ่งCOLLATION ส่วนใหญ่มีแนวโน้ม DB ของคุณใช้บางชุดท้องถิ่นของกฎ (เช่นในกรณีของฉัน: de_AT.UTF-8) ค้นหาด้วย:

SHOW lc_collate;

นี้จะทำให้การเรียงลำดับและดัชนีดูอัพช้า ยิ่งสายยาว (ชื่อสินค้า) ยิ่งแย่ หากคุณไม่สนใจกฎการเรียงในเอาต์พุตของคุณ (หรือลำดับการเรียงเลย) สิ่งนี้อาจเร็วขึ้นหากคุณเพิ่มCOLLATE "C":

SELECT DISTINCT ON (i.good COLLATE "C", i.the_date)
       i.the_date, p.the_date AS pricing_date, i.good, p.price
FROM   inventory  i
LEFT   JOIN price p ON i.good = p.good AND i.the_date >= p.the_date
ORDER  BY i.good COLLATE "C", i.the_date, p.the_date DESC;

โปรดสังเกตว่าฉันเพิ่มการเปรียบเทียบในสองแห่งได้อย่างไร
เร็วขึ้นเป็นสองเท่าในการทดสอบของฉันด้วยชื่อ 20k แถวและชื่อพื้นฐาน ('good123')

ดัชนี

หากแบบสอบถามของคุณควรใช้ดัชนีคอลัมน์ที่มีข้อมูลตัวอักษรจะต้องใช้การเปรียบเทียบที่ตรงกัน ( goodในตัวอย่าง):

CREATE INDEX inventory_good_date_desc_collate_c_idx
ON price(good COLLATE "C", the_date DESC);

อย่าลืมอ่านสองบทสุดท้ายของคำตอบที่เกี่ยวข้องใน SO:

คุณสามารถมีดัชนีหลายดัชนีที่มีการเรียงหน้าแตกต่างกันในคอลัมน์เดียวกัน - หากคุณต้องการสินค้าที่เรียงตามการเปรียบเทียบอื่น (หรือค่าเริ่มต้น) ในการสืบค้นอื่น ๆ

ทำให้ปกติ

สตริงที่ซ้ำซ้อน (ชื่อที่ดี) ยังขยายตารางและดัชนีของคุณซึ่งทำให้ทุกอย่างช้าลง ด้วยเค้าโครงตารางที่เหมาะสมคุณสามารถหลีกเลี่ยงปัญหาส่วนใหญ่ที่จะเริ่มต้นด้วย อาจมีลักษณะเช่นนี้:

CREATE TABLE good (
  good_id serial PRIMARY KEY
, good    text   NOT NULL
);

CREATE TABLE inventory (
  good_id  int  REFERENCES good (good_id)
, the_date date NOT NULL
, quantity int  NOT NULL
, PRIMARY KEY(good_id, the_date)
);

CREATE TABLE price (
  good_id  int     REFERENCES good (good_id)
, the_date date    NOT NULL
, price    numeric NOT NULL
, PRIMARY KEY(good_id, the_date));

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

CREATE INDEX price_good_date_desc_idx ON price(good, the_date DESC);

อีกครั้งการเปรียบเทียบจะต้องตรงกับคำค้นหาของคุณ (ดูด้านบน)

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

ข้อความค้นหาที่เป็นผลลัพธ์เหล่านี้เร็วกว่ามาก:

ไม่มีอยู่

SELECT i.the_date, p.the_date AS pricing_date, g.good, i.quantity, p.price
FROM   inventory  i
JOIN   good       g USING (good_id)
LEFT   JOIN price p ON p.good_id = i.good_id AND p.the_date <= i.the_date
AND    NOT EXISTS (
   SELECT 1 FROM price p1
   WHERE  p1.good_id = p.good_id
   AND    p1.the_date <= i.the_date
   AND    p1.the_date >  p.the_date
   );

ปิดกั้น

SELECT DISTINCT ON (i.the_date)
       i.the_date, p.the_date AS pricing_date, g.good, i.quantity, p.price
FROM   inventory  i
JOIN   good       g USING (good_id)
LEFT   JOIN price p ON p.good_id = i.good_id AND p.the_date <= i.the_date
ORDER  BY i.the_date, p.the_date DESC;

ซอ Fiddle


โซลูชั่นที่เร็วขึ้น

หากยังไม่เร็วพออาจมีวิธีแก้ปัญหาที่เร็วกว่า

JOIN LATERALแบบสอบถามย่อยCTE / / ที่สัมพันธ์กันแบบเรียกซ้ำ

โดยเฉพาะอย่างยิ่งสำหรับการแจกแจงข้อมูลที่มีราคามากมายต่อดี :

มุมมองที่ปรากฏ

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

Postgres 9.3+ มีการสนับสนุนอัตโนมัติสำหรับมุมมองที่ปรากฏ คุณสามารถใช้เวอร์ชันพื้นฐานในเวอร์ชันที่เก่ากว่าได้อย่างง่ายดาย


3
price_good_date_desc_idxดัชนีคุณแนะนำอย่างมากปรับปรุงประสิทธิภาพการทำงานสำหรับการสืบค้นข้อมูลของฉันที่คล้ายกัน แผนคิวรีของฉันเปลี่ยนจากราคา42374.01..42374.86ลงเป็น0.00..37.12!
cimmanon

@cimmanon: ดี! คุณสมบัติข้อความค้นหาหลักของคุณคืออะไร ไม่ใช่ EXISTS ปิดกั้นหรือไม่ จัดกลุ่มตาม?
Erwin Brandstetter

ใช้ DISTINCT ON
cimmanon

6

FYI ฉันใช้ mssql 2008 ดังนั้น Postgres จะไม่มีดัชนี "รวม" อย่างไรก็ตามการใช้การทำดัชนีพื้นฐานที่แสดงด้านล่างจะเปลี่ยนจาก hash joins เป็นการรวม joins ใน Postgres: http://explain.depesz.com/s/eF6 (ไม่มีดัชนี) http://explain.depesz.com/s/j9x ( กับดัชนีเกี่ยวกับเกณฑ์การเข้าร่วม)

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

create view mostrecent_pricing_dates_per_good as
select i.good,i.date i_date,max(p.date)p_date
  from inventory i
  join price p on i.good = p.good and i.date >= p.date
 group by i.good,i.date;

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

select i.good
       ,i.date inventory_date
       ,i.quantity
       ,p.date pricing_date
       ,p.price       
  from inventory i
  join price p on i.good = p.good
  join mostrecent_pricing_dates_per_good x 
    on i.good = x.good 
   and p.date = x.p_date
   and i.date = x.i_date

สิ่งนี้ให้ผลแผนการดำเนินการต่อไปนี้: http://sqlfiddle.com/#!3/24f23/1 ไม่มีการจัดทำดัชนี

... สแกนทั้งหมดด้วยการเรียงลำดับเต็ม สังเกตค่าใช้จ่ายประสิทธิภาพของการจับคู่แฮชใช้จำนวนมากของค่าใช้จ่ายทั้งหมด ... และเรารู้ว่าการสแกนตารางและการเรียงลำดับช้า (เทียบกับเป้าหมาย: ดัชนีค้นหา)

ตอนนี้เพิ่มดัชนีพื้นฐานเพื่อช่วยให้เกณฑ์ที่ใช้ในการเข้าร่วมของคุณ (ฉันไม่อ้างว่าเป็นดัชนีที่ดีที่สุด แต่พวกเขาแสดงให้เห็นถึงจุด): http://sqlfiddle.com/#!3/5ec75/1 ด้วยการจัดทำดัชนีขั้นพื้นฐาน

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

การทำซ้ำขั้นสุดท้ายใช้ "รวม" ในดัชนีเพื่อให้ง่ายต่อการเลื่อนและรับข้อมูลที่ต้องการเพิ่มเติมจากดัชนี ดังนั้นการค้นหาจึงหมดไป: http://sqlfiddle.com/#!3/5f143/1 ป้อนคำอธิบายรูปภาพที่นี่

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

  1. มันสร้างโครงสร้างข้อมูลที่เข้าใจได้ในฐานข้อมูลของคุณซึ่งง่ายต่อการเขียนและนำกลับมาใช้ใหม่ในส่วนอื่น ๆ ของแอปพลิเคชัน
  2. ตัวดำเนินการแบบสอบถามที่มีราคาแพงที่สุดทั้งหมดได้รับการแยกจากแผนแบบสอบถามโดยใช้การจัดทำดัชนีพื้นฐานบางอย่าง

3
นี่เป็นเรื่องปกติ (สำหรับ SQL-Server) แต่ปรับให้เหมาะสมสำหรับ DBMS ที่แตกต่างกันในขณะที่มีความคล้ายคลึงกันก็มีความแตกต่างที่ร้ายแรงเช่นกัน
ypercubeᵀᴹ

@ypercube ที่เป็นจริง ฉันเพิ่มคุณสมบัติบางประการเกี่ยวกับ Postgres ความตั้งใจของฉันคือกระบวนการคิดส่วนใหญ่ที่แสดงที่นี่จะนำไปใช้โดยไม่คำนึงถึงคุณลักษณะเฉพาะของ DBMS
cocogorilla

คำตอบนั้นมีความลึกมากดังนั้นจึงต้องใช้เวลาพอสมควรในการลองใช้ ฉันจะบอกให้คุณรู้ว่าฉันจะไปได้อย่างไร
Tom Ellis

5

หากคุณมี PostgreSQL 9.3 (วางจำหน่ายแล้ว) คุณสามารถใช้ LATERAL JOIN ได้

ฉันไม่มีวิธีทดสอบสิ่งนี้และไม่เคยใช้มาก่อน แต่จากสิ่งที่ฉันสามารถบอกได้จากเอกสารไวยากรณ์จะเป็นดังนี้:

SELECT  Inventory.Date,
        Inventory.Good,
        Inventory.Quantity,
        Price.Date,
        Price.Price
FROM    Inventory
        LATERAL
        (   SELECT  Date, Price
            FROM    Price
            WHERE   Price.Good = Inventory.Good
            AND     Price.Date <= Inventory.Date
            ORDER BY Price.Date DESC
            LIMIT 1
        ) p;

นี่คือพื้นเทียบเท่ากับSQL-Server ของ APPLYและมีตัวอย่างการทำงานของ SQL-Fiddleสำหรับการสาธิต


5

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

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

สิ่งแรกที่ต้องลองคือเพียงทำมุมมองและเข้าร่วม:

CREATE VIEW most_recent_rows AS
SELECT good, max(date) as max_date
FROM inventory
GROUP BY good;

สิ่งนี้ควรทำงานได้ดีเมื่อทำบางสิ่งเช่น:

SELECT price 
  FROM inventory i
  JOIN goods g ON i.goods = g.description
  JOIN most_recent_rows r ON i.goods = r.goods
 WHERE g.id = 123;

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

สิ่งที่สองที่คุณสามารถทำได้คือเพิ่มลงในตารางคลังคอลัมน์ most_recent bool และ

create unique index on inventory (good) where most_recent;

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

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

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

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

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

 SELECT price 
   FROM prices p
   JOIN goods g ON p.good = g.good
  WHERE g.id = 123 AND p."date" >= '2013-03-01'
  ORDER BY p."date" ASC LIMIT 1;

ด้วยดัชนีราคา (ดีวันที่) สิ่งนี้จะทำงานได้ดี

ฉันนี่เป็นตัวอย่างที่วางแผนไว้บางทีบางสิ่งที่ใกล้เคียงกับสิ่งที่คุณกำลังทำอยู่อาจช่วยได้


most_recentวิธีการควรจะทำงานได้ดีสำหรับราคาล่าสุดอย่าง ดูเหมือนว่า OP ต้องการราคาล่าสุดเมื่อเทียบกับแต่ละวันที่คลังโฆษณา
Erwin Brandstetter

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

@ChrisTravers: มันเป็นตัวอย่างที่วางแผนไว้ แต่ฉันไม่มีอิสระที่จะโพสต์สคีมาที่แท้จริงที่ฉันทำงานด้วย บางทีคุณอาจพูดอะไรเล็กน้อยเกี่ยวกับข้อบกพร่องเชิงปฏิบัติที่คุณเห็น
Tom Ellis

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

3

อีกวิธีหนึ่งคือการใช้ฟังก์ชั่นหน้าต่างlead()เพื่อรับช่วงวันที่สำหรับทุกแถวในราคาตารางแล้วใช้betweenเมื่อเข้าร่วมสินค้าคงคลัง ฉันเคยใช้สิ่งนี้ในชีวิตจริง แต่ส่วนใหญ่เป็นเพราะนี่เป็นความคิดแรกของฉันที่จะแก้ปัญหานี้

with cte as (
  select
    good,
    price,
    date,
    coalesce(lead(date) over(partition by good order by date) - 1
            ,Now()::date) as ndate
  from
    price
)

select * from inventory i join cte on
  (i.good = cte.good and i.date between cte.date and cte.ndate)

SqlFiddle


1

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

ดังนั้นสำหรับราคาสินค้าคงคลังของคุณ:

 Select i.date, p.Date pricingDate,
    i.good, quantity, price        
 from inventory I join price p 
    on p.good = i.good
        And p.Date = 
           (Select Max(Date from price
            where good = i.good
               and date <= i.Date)

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


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