เรียกใช้แบบสอบถามที่ซับซ้อนสำหรับทุกวันในช่วง


9

ฉันมีตารางคำสั่งซื้อแล้ว

   Column   |            Type             |                      Modifiers                      
------------+-----------------------------+-----------------------------------------------------
 id         | integer                     | not null default nextval('orders_id_seq'::regclass)
 client_id  | integer                     | not null
 start_date | date                        | not null
 end_date   | date                        | 
 order_type | character varying           | not null

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

 id | client_id | start_date |  end_date  | order_type 
----+-----------+------------+------------+------------
 17 |        11 | 2014-02-05 |            | standing
 18 |        15 | 2014-07-16 | 2015-07-19 | standing
 19 |        16 | 2015-04-01 |            | standing
 20 |        16 | 2015-07-18 | 2015-07-18 | temporary

ตัวอย่างเช่นใน2015-07-18ไคลเอนต์ 16 มีคำสั่งซื้อ # 20 เนื่องจากเป็นคำสั่งที่ใช้งานอยู่เพราะจะแทนที่คำสั่งซื้อที่ยืน # 19 ด้วยความเอะอะบางอย่างฉันพบวิธีที่มีประสิทธิภาพในการค้นหารหัสคำสั่งซื้อที่ใช้งานอยู่ในวันที่

    SELECT id from (
      SELECT
        id,
        first_value(id) OVER (PARTITION BY client_id ORDER BY order_type DESC) active_order_id
      FROM orders
      WHERE start_date <= ? and (end_date is null OR end_date >= ?)
    ) active_orders
    WHERE id = active_order_id

หากคุณค้นหาด้วย2015-07-18ตัวยึดตำแหน่งคุณจะได้รับ

 id 
----
 17
 18
 20

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

ตอนนี้ฉันต้องการ a เพื่อค้นหาคำสั่งซื้อที่ใช้งานอยู่ทั้งหมดสำหรับช่วงวันที่ที่เข้าร่วมกับวันที่ที่พวกเขาเปิดใช้งาน ตัวอย่างเช่นด้วยช่วงวันที่2015-07-18ถึง2015-07-19ฉันต้องการผลลัพธ์ต่อไปนี้

active_date | id 
------------+----
 2015-07-18 | 17
 2015-07-18 | 18
 2015-07-18 | 20
 2015-07-19 | 17
 2015-07-19 | 18
 2015-07-19 | 19

สั่งซื้อ 20 แทนที่สั่งซื้อ 19 แต่ไม่ได้อยู่ใน2015-07-182015-07-19

ฉันพบกับgenerate_series()ฉันสามารถสร้างช่วงของวันที่ แต่ฉันไม่ได้รู้วิธีการเข้าร่วมกับสิ่งนี้เพื่อรับตารางวันที่และรหัสการสั่งซื้อ ลางสังหรณ์ของฉันคือการเข้าร่วมไขว้ แต่ฉันไม่สามารถเข้าใจวิธีการทำงานในสถานการณ์นี้

ขอบคุณ

UPDATE เพิ่มซอ SQL


2
คุณสามารถแสดงข้อมูลตัวอย่างได้ไหม สิ่งที่ใช้งาน / ไม่ทำงานและชั่วคราวนี้ไม่ชัดเจนมากหลังจากอ่านครั้งแรก
dezso

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

ฉันอัปเดตคำถามของฉันพร้อมรายละเอียดเพิ่มเติมมากมายและใช่มีข้อ จำกัด ในข้อมูล
reconbot

คำตอบ:


5

ฉันจะใช้select distinct onแทนฟังก์ชั่นหน้าต่างจากนั้นก็เข้าร่วมวัน

select 
    distinct on (date, client_id) date, 
    id 
from orders
inner join generate_series('2015-07-18'::date, '2015-07-19'::date, '1 day') date
  on start_date <= date and (end_date is null or date <= end_date)
order by date, client_id, order_type desc

http://sqlfiddle.com/#!15/5a420/16/0

ฉันสามารถทำอย่างละเอียดมากขึ้นถ้าบางสิ่งไม่ชัดเจน


สิ่งนี้ไม่ครอบคลุมถึงคำสั่งชั่วคราว / คำสั่งยืน แต่สามารถทำได้หลังจากเข้าร่วม =)
reconbot

สิ่งนี้ระบุลำดับเดียวกันกับในแบบสอบถามหน้าต่างของคุณ ดังนั้นสำหรับใด ๆ (วันที่ client_id) มันจะเลือก order_type แรกในลำดับตัวอักษรที่กลับรายการ
Simon Perepelitsa

การรวมภายในนั้นสมบูรณ์แบบและการเลือกที่แตกต่างนั้นง่ายต่อการเข้าใจ (และทำงานได้ดีเช่นกัน) กว่าหน้าต่าง เหตุผลอื่นที่ฉันไม่ควรใช้ฟังก์ชั่น windowing?
reconbot

1
เกี่ยวกับมัน. ฉันคิดว่าdistinct onได้รับการปรับให้เหมาะสมยิ่งกว่าการค้นหาหน้าต่าง โดยวิธีการที่ฉันควรพูดถึงว่านี่เป็นปัญหา "top-in-group" ที่พบบ่อยใน SQL: stackoverflow.com/questions/3800551// …
Simon Perepelitsa

นั่นเป็นการอ่านที่ยอดเยี่ยมฉันมีการเรียนรู้ที่จะทำ หากคุณมีเวลาฉันมีรุ่นเพิ่มเติมของคำถามนี้ที่ใช้สิ่งที่ฉันเรียนรู้ที่นี่ dba.stackexchange.com/questions/108767/…ฉันแน่ใจว่าฉันจะกลับมาอัปเดตด้วยสิ่งที่ฉันเรียนรู้จากลิงค์นั้น และขอขอบคุณ
reconbot

0

เขียนฟังก์ชั่นที่ใช้วันที่เดียวเป็นพารามิเตอร์และส่งคืนรายการ date + id ที่มีคำสั่งซื้อ

จากนั้นใช้ generate_series ตามที่คุณแนะนำและเรียกใช้ฟังก์ชันในช่วงวันที่

นี่เป็นกลยุทธ์ทั่วไปเมื่อต้องรับมือกับเงื่อนไขที่ซับซ้อนใน SQL

ฉันได้รวมโค้ดไว้ด้านล่างแล้ว แต่คำตอบ SQL ข้างต้นนั้นง่ายกว่ามาก

นี่คือฟังก์ชั่น:

create or replace function o( date) returns setof INT AS '
SELECT id from (
 SELECT
  id,
  first_value(id) OVER (PARTITION BY client_id ORDER BY order_type DESC) active_order_id
 FROM orders
 WHERE start_date <= $1 and (end_date is null OR end_date >= $1)
) active_orders
WHERE id = active_order_id;
' LANGUAGE sql ;

และวิธีการโทร:

select distinct d, o(d::date) 
from generate_series('2015-07-18'::date, '2015-07-19'::date, '1 day') as d;

SQLFiddle


2
คุณอาจต้องการลบคำตอบนั้นออกพร้อมรายละเอียดตัวอย่างรหัส ฯลฯ ตามที่เป็นอยู่คำตอบนี้อาจถูกลบเพราะมันค่อนข้างคลุมเครือ
Max Vernon

คุณจะสามารถอัพเดตซอของฉันด้วยตัวอย่างได้หรือไม่? sqlfiddle.com/#!15/5a420/3/0
เริ่มต้นใหม่

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