MS SQL - Zbieranie danych do pewnej kwoty

0

Niedługo prawdopodobnie będę musiał wymyślić zapytanie SQL, które pobierze następujące dane:

Mamy tabelę z polami Id, Data i Kwota i tabela ma tysiące pozycji. Czy da się w prosty sposób w MSSQL zrobić select, który będzie odpowiedzią na następujące zadanie?

Posortuj tabelę według daty i pobierz tyle pierwszych pozycji, żeby suma kwoty była nie mniejsza od pewnej podanej X.

Jeżeli nie da się poprzez select, to może poprzez wprowadzenie do drugiej tabeli. Np. element "top" pobiera określoną liczbę pierwszych pozycji, a mi chodzi o pobranie tylu pozycji, aby spełnić określony warunek, może to być 1% wszystkich pozycji.

Mam pomysł, żeby zrobić kursor w pętli, który wypełnia drugą tabelę pamięciową (nazwa zaczyna się od #) z tymi samymi polami, gdzie pętla kończy się po osiągnięciu określonej kwoty lub po przejściu całej tabeli, potem select z tabeli #. Jednak czy da się to samo bardziej wydajnie, np. jednym zapytaniem select?

Drugi mój pomysł to wielokrotne odpalanie select top X, gdzie co uruchomienie, to X jest zwiększany o 1 i sumowana jest kwota, ale to też chyba mało wydajne i niezbyt dobre. Ewentualnie coś w rodzaju select top 5 * from Dane where Id not in (select top 4 X.Id from Dane X order by X.Data) order by Data, ale to też będzie wielokrotne robienie tego samego i obawiam się, że to takie se.

1

Przyjmując, że X = 100, oraz "nazwa_tabeli" to Twoja nazwa tabeli, to:


WITH my_pos AS (

SELECT Id, Data, (SELECT sum(e2.number_1) FROM nazwa_tabeli e2 WHERE  e2.Data >= e1.Data) as top_sum 
FROM nazwa_tabeli e1 ORDER BY Data DESC

)

SELECT my_pos.* FROM my_pos WHERE top_sum <= 100 ORDER BY top_sum ASC;

Trochę to prymitywne, można na pewno lepiej, żeby nie trzeba było skanować całej tabeli oraz nie sumować całości poprzednich wierszy dla każdego wiersza, ale działa, chociaż ja bym za to dał ocenę szkolną 2 - 2,5 w skali 1-6

Tutaj trzeba zrobić funkcję / procedurę która iterując po kolei po rekordach, dokonuje zwiększenia licznika, po czym po osiągnięciu X kończy pracę i zwraca listę wierszy.

PS. Nazwa kolumny "Data" nie jest zbyt dobra bo to słowo kluczowe w SQL. Po drugie tak samo zaczynanie nazw kolumn z dużej litery - wszystko się robi małymi.

1

Jeżeli to wersja >= 2012

Select * from 
(Select *, SUM (kwota) OVER (ORDER BY [data)] AS Rs from tabela) ft
Where Rs < 10000
0

Testowałem to, co proponuje Panczo i znalazłem rozwiązanie. W moim przypadku chodziło o uzyskanie tylu zapisów, aby suma była równa lub minimalnie powyżej określonej kwoty

Testowałem na konkretnym przykładzie:

-- Tabela do testów
create table #A
(
 SortKey int,
 Val decimal(38,8)
)

-- Dane testowe
insert into #A (SortKey, Val) values ( 1, 100)
insert into #A (SortKey, Val) values ( 2, 300)
insert into #A (SortKey, Val) values ( 3, 600)
insert into #A (SortKey, Val) values ( 4, 400)
insert into #A (SortKey, Val) values ( 5, 100)
insert into #A (SortKey, Val) values ( 6, 200)
insert into #A (SortKey, Val) values ( 7, 100)
insert into #A (SortKey, Val) values ( 8, 500)
insert into #A (SortKey, Val) values ( 9, 800)
insert into #A (SortKey, Val) values (10, 400)
insert into #A (SortKey, Val) values (11, 900)
insert into #A (SortKey, Val) values (12, 400)
insert into #A (SortKey, Val) values (13, 200)
insert into #A (SortKey, Val) values (14, 600)
insert into #A (SortKey, Val) values (15, 400)
insert into #A (SortKey, Val) values (16, 700)
insert into #A (SortKey, Val) values (17, 200)
insert into #A (SortKey, Val) values (18, 100)
insert into #A (SortKey, Val) values (19, 300)
insert into #A (SortKey, Val) values (20, 600)

-- Pobieranie tylu pierwszych pozycji w określonej kolejności, żeby suma zmieściła się w 3000
select SortKey, Val from 
(select *, sum(Val) over (order by SortKey) as XX from #A) SS
where XX < 3000

-- Pobieranie tylu pierwszych pozycji w określonej kolejności, żeby suma była co najmniej 3000
select top (select (count(*) + 1) from 
(select *, sum(Val) over (order by SortKey) as XX from #A) SS
where XX < 3000) * from #A order by SortKey

Jak widać, wymyśliłem, że wystarczy policzyć, ile pozycji składa się na kwotę mieszczącą się w sumie i wystarczy pobrać o jedną pozycję więcej.

0

To nie lepej tak:

select 
	*
FROM (
	select 
		*
		,sum(Val) over (order by SortKey) - val as RS
	from 
		#A
	) dt
where
	rs < 3000

1 użytkowników online, w tym zalogowanych: 0, gości: 1