Aralıktaki her tarih için karmaşık bir sorgu çalıştırma


9

Bir sipariş tablom var

   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

Veriler, bir client_id için örtüşmeyen duran siparişlere ve bazen eşleşen bir client_id'e sahip olduklarında başlangıç_tarihindeki duran siparişi geçersiz kılan geçici bir siparişe sahiptir. Aynı türden siparişlerin çakışmasını önleyen uygulama düzeyinde kısıtlamalar vardır.

 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

Örneğin, 2015-07-1816 numaralı istemcide etkin sipariş olarak # 20 numaralı sipariş vardır, çünkü 19 numaralı siparişi geçersiz kılar. Bazı yaygara ile etkin bir sipariş kimliği bir tarihte sorgulamak için etkili bir yol buldum.

    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

Bunu 2015-07-18yer tutucular olarak sorgularsanız ,

 id 
----
 17
 18
 20

Diğer fikirlerim ile karşılaştırıldığında bu sorgudaki sorgu planı (bir tarihte bir müşteri için geçici sipariş sayısını sayan alt sorgular gibi) oldukça küçük ve bundan oldukça memnunum. (masanın tasarımı, heyecan duymuyorum)

Şimdi, etkin oldukları tarihlerle birleştirilen bir tarih aralığındaki tüm etkin siparişleri bulmak için a'ya ihtiyacım var. Örneğin, tarih aralığına sahip 2015-07-18üzere 2015-07-19I aşağıdaki sonucu istiyoruz.

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

Sipariş 20 geçersiz kılma üzerinde 19 sipariş 2015-07-18ancak üzerinde 2015-07-19.

Ben generate_series()bir tarih aralığı oluşturabilir ile buldum , ama tarihler ve sipariş kimlikleri bir tablo almak için bu ile katılmak nasıl bir ipucu yok. Benim önsezim çapraz bir birleşimdir, ancak bu durumda bunu nasıl çalıştıracağımı anlayamıyorum.

Teşekkürler

GÜNCELLEME Bir sql keman eklendi .


2
Bazı örnek verileri gösterebilir misiniz? Bu aktif / aktif olmayan ve geçici şeyler ilk okumadan sonra çok net değildir.
dezso

Evet, net değil. Sorgunuz her müşteri için bir sipariş bulacaktır ve bu belirleyici görünmemektedir. Bir müşteri için aynı türde 2 veya daha fazla sipariş varsa, ikisinden hangisi iade edilirse keyfi olur ve yürütme başına değişir. Yani, masada bize söylemediğiniz bazı kısıtlamalarınız var veya sorgunuz doğru değil.
ypercubeᵀᴹ

Sorumu çok daha fazla ayrıntıyla güncelledim ve evet veriler üzerinde kısıtlamalar var.
reconbot

Yanıtlar:


5

Ben select distinct onpencere işlevi yerine kullanmak , sonra sadece gün katılın.

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

Bir şey net değilse daha fazla ayrıntı verebilirim.


Bu geçici emri / daimi emri kapsamaz, ancak katılma işleminden sonra yapılabilir =)
reconbot

Bu, pencere sorgunuzdakiyle aynı sırayı belirtir. Bu nedenle, herhangi bir tarih (client_id) ilk alfabetik sırayla ilk sipariş_türünü seçer.
Simon Perepelitsa

İç birleşim mükemmeldir ve seçim farkını anlamak pencereden çok daha kolaydır (ve aynı şekilde performans gösterir). Pencereleme işlevlerini kullanmamamın başka bir nedeni var mı?
reconbot

1
Bu kadar. Bence distinct onpencere sorgusundan bile daha optimize edilmiş. Bu arada, bunun SQL'de yaygın bir "grup içi" sorunu olduğunu belirtmeliyim: stackoverflow.com/questions/3800551/…
Simon Perepelitsa

Bu harika bir okuma, yapacak biraz çalışmam var. Biraz zamanınız varsa, bu sorunun burada öğrendiklerimi kullanan genişletilmiş bir versiyonuna sahibim. dba.stackexchange.com/questions/108767/… Eminim bu bağlantıdan öğrendiklerimle güncellemeye geri döneceğim. Ve Teşekkürler
reconbot

0

Tek bir tarihi parametre olarak alan ve sipariş içeren tarih + kimliklerinin listesini döndüren bir işlev yazın.

Ardından, önerdiğiniz gibi genera_series kullanın ve işlevi tarih aralığı boyunca çağırın.

Bu, SQL'deki karmaşık koşullarla uğraşırken yaygın bir stratejidir.

Aşağıda bazı kodlar ekledim, ancak yukarıdaki SQL yanıtı çok daha basit.

İşte işlevi:

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 ;

Ve nasıl çağırılır:

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

SQLFiddle


2
Bu yanıtı bazı ayrıntılar, örnek kod, vb. İle temizlemek isteyebilirsiniz. Olduğu gibi, bu cevap oldukça belirsiz olduğu için silinebilir.
Max Vernon

Kemanımı bir örnekle güncelleyebilir misiniz? sqlfiddle.com/#!15/5a420/3/0
reconbot

Cevabımı bazı kodları içerecek şekilde güncelledim, ancak yukarıdaki cevap daha basit.
Don Drake
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.