2017-12-06 1 views
1

Die Abfrage, die ich entwickeln muss, ist, Websites aufzulisten, die keine Datensätze (Retail-Transaktionen) für ein bestimmtes Datum haben. Ich konnte dies für eine bestimmte Site erreichen, aber ich weiß, dass die Abfrage effizienter mit Joins geschrieben werden kann, aber mein Ausflug in Innere und Äußere hat nicht viel Spaß gemacht.Optimieren Sie T-SQL-Abfrage

Hier ist, was ich bisher habe:

DECLARE @StartDate DATE = '2017-11-01', 
    @EndDate DATE = '2017-11-30'; 

SELECT tx.Txndate, count(*) as txCount 
FROM [Report].[dbo].[FactTransactions] tx 
where tx.Site = 2 
and tx.TxnDate between @StartDate and @EndDate 
group by tx.TxnDate 
union all 
select db.daybookdate, 0 as txCount 
from DimDaybook db 
where db.daybookdate between @StartDate and @EndDate 
and NOT EXISTS (SELECT 1 FROM [Report].[dbo].[FactTransactions] AS t WHERE t.TxnDate = db.daybookdate and t.txndate between @StartDate and @EndDate and t.site = 2) 
order by tx.Txndate 

Das gibt mir die Ergebnismenge für Website 2

Txndate txCount 
2017-11-01 1691 
2017-11-02 1657 
2017-11-03 1835 
2017-11-04 1587 
2017-11-05 1489 
2017-11-06 1544 
2017-11-07 1525 
2017-11-08 1782 
2017-11-09 1848 
2017-11-10 1990 
2017-11-11 0 
2017-11-12 0 

Was ich wirklich eine Ergebnismenge haben möchten, die wie folgt aussieht und läuft viel viel schneller als mein Hack (derzeit etwa 2 Minuten - für den Kontext der Transaktion Tabelle hat 83.486.412 Datensätze)

Site Date Transactions 
2 11/11/17 0 
2 12/11/27 0 
3 12/11/17 0 
22 1/11/17  0 
+0

Was ist die gewünschte Logik hinter der zweiten Ergebnismenge? Möchten Sie nur Resultsets mit 0 Transaktionen pro Tag pro Site? –

+0

Persönlicher Vorschlag außerhalb des Bereichs der Frage, ich würde die 'between'-Bedingung ändern, um explizit zu zeigen, was Sie suchen, da das Verhalten von' between' nicht über alle db-Engines konsistent ist. Manchmal ist es inklusive, manchmal nicht. – user2366842

Antwort

0

Generieren Sie alle Zeilen mit einem cross join. Dann filtern diejenigen, die mit Daten aus:

select s.site, db.daybookdate 
from DimDaybook db cross join 
    (select distinct site 
     from [Report].[dbo].[FactTransactions] ft 
    ) s 
where db.daybookdate between @StartDate and @EndDate and 
     not exists (select 1 
        from [Report].[dbo].[FactTransactions] ft 
        where ft.TxnDate = db.daybookdate and 
         ft.site = s.site 
       ) 
order by tx.Txndate 
+0

Ich danke Ihnen dafür - perfekt !. Ich habe eine kleine Änderung an der Order-by-Klausel vorgenommen, um es zum Laufen zu bringen. 45secs - SQL magic am besten – 300baud

0

Eine Möglichkeit, die folgenden sein:

DECLARE @StartDate DATE = '2017-11-01', @EndDate DATE = '2017-11-30'; 
With cte as (
SELECT tx.Txndate, count(*) as txCount FROM 
[Report].[dbo].[FactTransactions] tx where tx.Site = 2 and tx.TxnDate between @StartDate and @EndDate group by tx.TxnDate) 
Select db.daybookdate, isnull(txCount,0) from DimDaybook db left join cte on db.daybookdate = cte.Txndate 

Ich vermisse die Website id aber es gibt nirgendwo kann ich es von Ihrem Beispielcode erhalten.

+0

Es ist in der where-Klausel ('where tx.Site = 2'). –

+1

Großartig! Haben Sie einen Tisch, an dem Sie die Standorte beschreiben? Wie sonst könnten Sie die Website-IDs kennen, die innerhalb dieses Intervalls keine Transaktionen durchgeführt haben? –

1

Da Sie bereits eine Liste aller verfügbaren Tage haben (DimDaybook), müssen Sie nur noch "left join" eingeben und dann NULL mit Null ersetzen.

Seien Sie vorsichtig mit Datumsbereichen, die Verwendung von "between" klingt vielleicht nach einer guten Methode, aber es ist nicht der beste Weg. Anstatt zu versuchen, den letzten Tag im November anzugeben, geben Sie stattdessen nur den ersten Dezember an. Verwenden Sie dann eine Kombination aus >= und < mit Ihren Datumsparametern. Dann funktioniert Ihr Datumsbereich für jeden Datentyp Datum/Uhrzeit (DatumZeit2, DatumZeit, smalldatime, Datum)

DECLARE @StartDate date = '2017-11-01' 
     , @EndDate date = '2017-12-01'; -- this has changed! 

SELECT 
     db.daybookdate 
    , COALESCE(txCount, 0) 
FROM DimDaybook db 
LEFT JOIN (
        SELECT 
         tx.Txndate 
         , COUNT(*) AS txcount 
        FROM [Report].[dbo].[FactTransactions] tx 
        WHERE tx.Site = 2 
        AND tx.TxnDate >= @StartDate AND tx.TxnDate < @EndDate 
        GROUP BY 
         tx.TxnDate 
    ) c ON db.daybookdate = c.Txndate 
WHERE db.daybookdate >= @StartDate AND db.daybookdate < @EndDate 
AND tx.Txndate IS NULL 

Wenn Sie ein Datum Referenz wie '2017-11-30' die Tageszeit etablieren wird bei 00:00:00+0000000 automatisch eingestellt. Für Daten, auf die Sie möglicherweise stoßen, ist ein Datum von '2017-11-30', aber eine Zeit > 00:00:00, dass Daten ausgeschlossen wären. Kurz gesagt: wird die Dauer dieses Tages ignoriert. Dies kann leicht überwunden werden, indem einfach das Enddatum "up" auf den nächsten Tag verschoben wird und weniger als dieses Datum verwendet wird, und dazu müssen Sie die Verwendung von "between" vermeiden.