2013-07-30 24 views
5

Próbuję zrobić kwerendę w tej tabeli:Grupowanie według zakresu dat w T-SQL

Id  startdate  enddate  amount 
1  2013-01-01 2013-01-31  0.00 
2  2013-02-01 2013-02-28  0.00 
3  2013-03-01 2013-03-31  245 
4  2013-04-01 2013-04-30  529 
5  2013-05-01 2013-05-31  0.00 
6  2013-06-01 2013-06-30  383 
7  2013-07-01 2013-07-31  0.00 
8  2013-08-01 2013-08-31  0.00 

Chcę uzyskać dane wyjściowe:

2013-01-01   2013-02-28   0 
2013-03-01   2013-06-30   1157 
2013-07-01   2013-08-31   0 

chciałem uzyskać ten wynik więc wiedziałbym, kiedy zaczęły pojawiać się pieniądze i kiedy to się skończyło. Interesuje mnie również liczba miesięcy, zanim zaczęły pojawiać się pieniądze (co wyjaśnia pierwszy wiersz) oraz liczba miesięcy, w których pieniądze się zatrzymały (co wyjaśnia, dlaczego jestem zainteresowany trzecim wierszem od lipca 2013 r. Do sierpnia 2013 r.).).

Wiem, że mogę używać min i max w datach i sumach na kwotę, ale nie mogę wymyślić, jak uzyskać podzielone w ten sposób rekordy.
Dzięki!

+1

dzięki za edycji Mahmoud Gamal. Próbowałem zadać to pytanie z mojego telefonu z Androidem. –

+2

Na podstawie tego, co chcesz zrobić w grupie? –

+2

Wygląda na to, że chcesz zgrupować kolejne wiersze zera i niezerowe, ale dlaczego nie oddzielisz majowego wiersza? –

Odpowiedz

2

Oto jeden pomysł (i a fiddle iść z nim):

;WITH MoneyComingIn AS 
(
    SELECT MIN(startdate) AS startdate, MAX(enddate) AS enddate, 
     SUM(amount) AS amount 
    FROM myTable 
    WHERE amount > 0 
) 
SELECT MIN(startdate) AS startdate, MAX(enddate) AS enddate, 
    SUM(amount) AS amount 
FROM myTable 
WHERE enddate < (SELECT startdate FROM MoneyComingIn) 
UNION ALL 
SELECT startdate, enddate, amount 
FROM MoneyComingIn 
UNION ALL 
SELECT MIN(startdate) AS startdate, MAX(enddate) AS enddate, 
    SUM(amount) AS amount 
FROM myTable 
WHERE startdate > (SELECT enddate FROM MoneyComingIn) 

i drugi, bez użycia UNION (fiddle):

SELECT MIN(startdate), MAX(enddate), SUM(amount) 
FROM 
(
    SELECT startdate, enddate, amount, 
    CASE 
     WHEN EXISTS(SELECT 1 
        FROM myTable b 
        WHERE b.id>=a.id AND b.amount > 0) THEN 
      CASE WHEN EXISTS(SELECT 1 
          FROM myTable b 
          WHERE b.id<=a.id AND b.amount > 0) 
       THEN 2 
       ELSE 1 
      END 
     ELSE 3 
    END AS partition_no 
    FROM myTable a 
) x 
GROUP BY partition_no 

chociaż przypuszczam, jak napisano go zakłada, że ​​Id są w porządku. Możesz zastąpić to z ROW_NUMBER() OVER(ORDER BY startdate).

+0

+1 Żadne z tych rozwiązań nie musi używać 'UNION' lub zmiennych. – Yuck

+0

dobre rozwiązanie bez UNION. –

0

Jeśli wszystko, co chcesz zrobić, to sprawdzić, kiedy pieniądze zaczęły napływać, a kiedy przestał, to może pracować dla Ciebie:

select 
    min(startdate), 
    max(enddate), 
    sum(amount) 
where 
    amount > 0 

Nie byłoby to okresy, w których nie było pieniędzy w najbliższych chociaż .

+0

To da ci pierwszą datę początkową, ostatnią datę końcową i całkowitą: [link] (http://www.sqlfiddle.com/#!3/c0d17/4) – Sam

+0

Tak, ale jak OP stwierdził: "Chciałem aby uzyskać ten wynik, więc wiedziałbym, kiedy pieniądze zaczęły pojawiać się i kiedy się zatrzymywały ", co robi. Minus puste okresy przed i po, to jest :) –

1

Coś takiego powinno zrobić:

select min(startdate), max(enddate), sum(amount) from paiements 
    where enddate < (select min(startdate) from paiements where amount >0) 
union 
select min(startdate), max(enddate), sum(amount) from paiements 
    where startdate >= (select min(startdate) from paiements where amount >0) 
    and enddate <= (select max(enddate) from paiements where amount >0) 
union 
select min(startdate), max(enddate), sum(amount) from paiements 
    where startdate > (select max(enddate) from paiements where amount >0) 

Ale dla tego rodzaju raportów, to chyba bardziej wyraźny przy użyciu wielu zapytań.

1

to robi to, co chcesz:

-- determine the three periods 
DECLARE @StartMoneyIn INT 
DECLARE @EndMoneyIn INT 

SELECT @StartMoneyIn = MIN(Id) 
FROM [Amounts] 
WHERE amount > 0 

SELECT @EndMoneyIn = MAX(Id) 
FROM [Amounts] 
WHERE amount > 0 

-- retrieve the amounts 
SELECT MIN(startdate) AS startdate, MAX(enddate) AS enddate, SUM(amount) AS amount 
FROM [Amounts] 
WHERE Id < @StartMoneyIn 
UNION 
SELECT MIN(startdate), MAX(enddate), SUM(amount) 
FROM [Amounts] 
WHERE Id >= @StartMoneyIn AND Id <= @EndMoneyIn 
UNION 
SELECT MIN(startdate), MAX(enddate), SUM(amount) 
FROM [Amounts] 
WHERE Id > @EndMoneyIn 
4
with CT as 
(
    select t1.*, 
      (select max(endDate) 
      from t 
      where startDate<t1.StartDate and SIGN(amount)<>SIGN(t1.Amount) 
      ) as GroupDate 
    from t as t1 
) 
select min(StartDate) as StartDate, 
     max(EndDate) as EndDate, 
     sum(Amount) as Amount 
from CT 
group by GroupDate 
order by StartDate 

SQLFiddle demo

+0

Bardzo przydatne zapytanie. Chociaż ten zwraca rekordy dla zer pomiędzy. –

0

Jeśli nie dbają o sumie w tym okresie, ale chcą tylko rekordy, gdzie idziesz od 0 do czegoś i vica versa, możesz zrobić coś tak szalonego:

select * 
from MoneyTable mt 
where exists (select * 
       from MoneyTable mtTemp 
       where mtTemp.enddate = dateadd(day, -1, mt.startDate) 
       and mtTemp.amount <> mt.amount 
       and mtTemp.amount * mt.amount = 0) 

Lub jeśli musisz uwzględnić pierwsze Rekord:

select * 
from MoneyTable mt 
where exists (select * 
       from MoneyTable mtTemp 
       where mtTemp.enddate = dateadd(day, -1, mt.startDate) 
       and mtTemp.amount <> mt.amount 
       and mtTemp.amount * mt.amount = 0) 
or not exists (select * 
       from MoneyTable mtTemp 
       where mtTemp.enddate = dateadd(day, -1, mt.startDate)) 

Sql Fiddle

Powiązane problemy