SQL

2016-03-21 12 views
4

kullanarak bir kaydın en son sürümü için verimli bir sorgu Sorgulama, tüm geçerli tarihler için (son günün saat dizisi sonu) bir kaydın son sürümü için bir tablo sorgular. Aşağıdaki örnek, elde etmeye çalıştığım şeyi göstermektedir.SQL

Sorunuz, tablo tasarımının (birincil anahtar vb.) Ve LEFT OUTER JOIN sorgusunun bu hedefi en verimli şekilde gerçekleştirip gerçekleştirmediğidir.

CREATE TABLE [PriceHistory] 
(
    [RowID] [int] IDENTITY(1,1) NOT NULL, 
    [ItemIdentifier] [varchar](10) NOT NULL, 
    [EffectiveDate] [date] NOT NULL, 
    [Price] [decimal](12, 2) NOT NULL, 

    CONSTRAINT [PK_PriceHistory] 
     PRIMARY KEY CLUSTERED ([ItemIdentifier] ASC, [RowID] DESC, [EffectiveDate] ASC) 
) 

INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-15',5.50) 
INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-16',5.75) 
INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-16',6.25) 
INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-17',6.05) 
INSERT INTO [PriceHistory] VALUES ('ABC','2016-03-18',6.85) 
GO 

SELECT 
    L.EffectiveDate, L.Price 
FROM 
    [PriceHistory] L 
LEFT OUTER JOIN 
    [PriceHistory] R ON L.ItemIdentifier = R.ItemIdentifier 
        AND L.EffectiveDate = R.EffectiveDate 
        AND L.RowID < R.RowID 
WHERE 
    L.ItemIdentifier = 'ABC' and R.EffectiveDate is NULL 
ORDER BY 
    L.EffectiveDate 

Takip: Tablo fiyat verilerinin değerinde dacades ile ItemIdentifiers her binlerce içerebilir. Denetim nedenleriyle verilerin tarihsel sürümünün korunması gerekir. Tabloyu sorguladığımı ve verileri bir raporda kullandığımı varsayalım. Rapor oluşturulduğunda @MRID = Max(RowID) depolarım. Şimdi '2016-03-16' tarihinde 'ABC' fiyatı sonraki bir tarihte düzeltilirse, @MRID kullanarak sorguyu değiştirebilir ve daha önce çalıştırdığım raporu çoğaltabilirim.

+0

Neden katılmak yapıyorsun? İlk 1 yeterince seçilmiyor mu? –

+1

ddl ve örnek verilerini gönderdiğiniz için teşekkür ederiz. Bu çok daha kolay yardımcı olur. Keşke herkesin bu şekilde olmasını isterdim! –

cevap

2

@ SeanLange en Yanıta biraz değiştirilmiş bir sürümü yerine ürün başına ki, size tarih başına son satırı verecektir:

with sortedResults as 
(
    select * 
     , ROW_NUMBER() over(PARTITION by ItemIdentifier, EffectiveDate 
          ORDER by ID desc) as RowNum 
    from PriceHistory 
) 

select ItemIdentifier, EffectiveDate, Price 
from sortedResults 
where RowNum = 1 
order by 2 
+1

Bu, OP'nin aradığı şey. Tek öneri, sıra pozisyonuna göre sipariş vermemek. Sütun adıyla sipariş vermek daha temiz ve güvenlidir. –

+0

@SeanLange Katılıyorum.ORDER BY'i sıralı bir konumla kullanarak, kod yeterince okunamaz ve ayrıca SELECT'te sütunların sırasını değiştirirseniz, sorgu yanlış sonuçlara dönecektir. –

2

Tablonuzda 1'den fazla ItemIdentifier var. Tablonuzdaki verilerin sürümlerini tuttuğunuz için tasarımınız biraz sorunlu. Bununla birlikte, her bir ItemIdentifier için en yeni olanı almak için oldukça kolay bir şekilde bunu yapabilirsiniz.

with sortedResults as 
(
    select * 
     , ROW_NUMBER() over(PARTITION by ItemIdentifier order by EffectiveDate desc) as RowNum 
    from PriceHistory 
) 
select * 
from sortedResults 
where RowNum = 1 
+0

Yalnızca belirli bir ItemIdentifier öğesinin değerini istiyorlardı. –

+0

Bu sorguda bana 'ABC' en son fiyatı veriyor. Tabloda mevcut olan her tarih için en son fiyata ihtiyacım var. Sağladığım sorgu doğru sonuçları üretiyor: tarih başına yalnızca bir fiyatla bir zaman dizisi. – c31983

+0

Bu, sonuçların yalnızca ABC'yi göstermeyecek şekilde sınırlandırılmaz. Tüm tablodaki her bir ItemIdentifier için en son fiyatı gösterecektir. Sadece tek bir öğe istiyorsanız, o zaman TOP önerilerinden birini kullanmalısınız. –

1

Kısa yanıt, no.

Varolan dizinlerinize bağlı olarak aynı tablonun iki kez ve muhtemelen bir ilmekli tablo taraması oluşturuyorsunuz. En iyi durumda, döngülenmiş bir dizin aramasına neden oluyorsunuz ve sonra satırların çoğunu dışarı atıyorsunuz.

Bu, sorduklarınız için en verimli sorgu olacaktır.

SELECT 
    L.EffectiveDate, 
    L.Price 
FROM 
    (
     SELECT 
      L.EffectiveDate, 
      L.Price, 
      ROW_NUMBER() OVER (
       PARTITION BY 
        L.ItemIdentifier, 
        L.EffectiveDate 
       ORDER BY RowID DESC) RowNum 
     FROM [PriceHistory] L 
     WHERE L.ItemIdentifier = 'ABC' 
    ) L 
WHERE 
    L.RowNum = 1; 
+0

Önerdiğiniz ilk sorgu yalnızca tek bir kayıt döndürür. Her bir tarih için en son değeri içeren bir zaman dizisine ihtiyacım var. Gönderiye dahil ettiğim sorgu doğru sonuçları üretir. İkinci önerinizde atıfta bulunulan CROSS APPLY operatörüne aşina değilim ve bunu yürütmek için sorun yaşıyorum. [Öğe] nereden geliyor? – c31983

+0

Tamam, temelde diğer cevapların nasıl yapılandırıldığını eşleştirmek için düzelttim. Şimdi bu sonuç kümesine başka bir tabloya katılmayı veya daha büyük bir sorguda kullanmayı denediğinizde sorun gelir. CROSS APPLY'nin devreye girebileceği yer burası. Bu öneriyi, aradığınız şeyin bir parçası gibi gelmediğinden kaldırdım. –