Updating Column - oracle

I created a table with the following attributes :
TABLE "VENDORACCOUNT"
( "VEN_ACCOUNTID"
"VEN_REGNO"
"VEN_TXDATE"
"VEN_INVOICE_REFNO"
"TOTALAMOUNT"
"PAID_TOVEN"
"BALANCE"
)
I take the TOTALAMOUNT column value through a POPUP LOV from another table on the basis of its VEN_INVOICE_REFNO value. Here the scenario is that the TOTALAMOUNT column value is subtracted from the PAID_TOVEN column value. But the next time I select the TOTALAMOUNT value it does not show me the updated value. It shows me the old value as shown in the report below.
Query of Report:
select "VEN_ACCOUNTID",
"VEN_REGNO" ,
"VEN_TXDATE" ,
"VEN_INVOICE_REFNO" as ,
"TOTALAMOUNT" as ,
"PAID_TOVEN" as ,
TOTALAMOUNT-PAID_TOVEN as "Balance"
from "VENDORACCOUNT"
In the above report I want that whenever I do the second entry it should show me the subtracted or updated value ie 1800 instead of 2800 and 4550 instead of 9550 respectively. So the next time I can subtract the amount from 1800 and 4550.
I created this trigger
create or replace trigger "VENDORACCOUNT_T2"
BEFORE
insert or update or delete on "VENDORACCOUNT"
for each row
begin
DECLARE new_balance INT;
DECLARE new_total INT;
DECLARE new_paid INT;
SELECT balance INTO old_balance,
total INTO old_total,
PAID_TOVEN INTO new_paid
FROM vendoraccount
WHERE ven_regno = new.ven_regno
AND VEN_INVOICE_REFNO = new.VEN_INVOICE_REFNO;
UPDATE vendoraccount SET TOTALAMOUNT = old_total + old_balance - new_paid,
balance = TOTALAMOUNT - new_paid
WHERE VEN_REGNO= new.VEN_REGNO
AND VEN_INVOICE_REFNO = new.VEN_INVOICE_REFNO;
end;
and am getting this error:
ERROR: PLS-00103: 'Encountered the symbol "DECLARE" when expecting one
of the following: begin function pragma procedure subtype type current curs'

I think you don't need a trigger. You can create a view based on the below query and then create the RECORD GROUP for your LOV based on the VIEW. The query would look like-
select accid,regno,inv_refno,LAG(bal,1,totalamount) OVER (PARTITION BY regno ORDER BY accid) "TOTALAMOUNT", paid_toven, bal
from (with temp_data as
(select 2 accid,3 regno, 16 inv_refno, 2800 totalamount, 1000 paid_toven from dual
union
select 3 accid,3 regno, 16 inv_refno, 2800 totalamount, 2000 paid_toven from dual
union
select 4 accid,8 regno, 22 inv_refno, 9550 totalamount, 5000 paid_toven from dual
union
select 5 accid,8 regno, 22 inv_refno, 9550 totalamount, 5000 paid_toven from dual
union
select 6 accid,8 regno, 22 inv_refno, 9550 totalamount, 8000 paid_toven from dual)
select accid,regno,inv_refno,totalamount,paid_toven,totalamount-paid_toven bal
from temp_data);
The output is -
ACCID REGNO INV_REFNO TOTALAMOUNT PAID_TOVEN BAL
----- ----- --------- ----------- ---------- ---
2 3 16 2800 1000 1800
3 3 16 1800 2000 800
4 8 22 9550 5000 4550
5 8 22 4550 5000 4550
6 8 22 4550 8000 1550
So based on your table the query would be-
select accid,regno,inv_refno,LAG(bal,1,totalamount) OVER (PARTITION BY regno ORDER BY accid) "TOTALAMOUNT", paid_toven, bal
from (select accid,regno,inv_refno,totalamount,paid_toven,totalamount-paid_toven bal
from VENDORACCOUNT);
The query does pretty much what you want, but your sample data does not look right. This is actually one example of a running total.

Related

PL SQL function that includes multiple tables

I'm new to PL SQL and have to write a function, which has customer_id as an input and has to output a product_name of the best selling product for that customer_id.
The schema looks like this:
I found a lot of simple examples where it includes two tables, but I can't seem to find one where you have to do multiple joins and use a function, while selecting only the best selling product.
I could paste a lot of very bad code here and how I tried to approach this, but this seems to be a bit over my head for current knowledge, since I've been learning PL SQL for less than 3 days now and got this task.
With some sample data (minimal column set):
SQL> select * from products order by product_id;
PRODUCT_ID PRODUCT_NAME
---------- ----------------
1 BMW
2 Audi
SQL> select * From order_items;
PRODUCT_ID CUSTOM QUANTITY UNIT_PRICE
---------- ------ ---------- ----------
1 Little 100 1
1 Little 200 2
2 Foot 300 3
If we check some totals:
SQL> select o.product_id,
2 o.customer_id,
3 sum(o.quantity * o.unit_price) total
4 from order_items o
5 group by o.product_id, o.customer_id;
PRODUCT_ID CUSTOM TOTAL
---------- ------ ----------
2 Little 400
1 Little 100
2 Foot 900
SQL>
It says that
for customer Little, product 2 was sold with total = 400 - that's our choice for Little
for customer Little, product 1 was sold with total = 100
for customer Foot, product 2 was sold with total = 900 - that's our choice for Foot
Query might then look like this:
temp CTE calculates totals per each customer
rank_them CTE ranks them in descending order per each customer; row_number so that you get only one product, even if there are ties
finally, select the one that ranks as the highest
SQL> with
2 temp as
3 (select o.product_id,
4 o.customer_id,
5 sum(o.quantity * o.unit_price) total
6 from order_items o
7 group by o.product_id, o.customer_id
8 ),
9 rank_them as
10 (select t.customer_id,
11 t.product_id,
12 row_number() over (partition by t.customer_id order by t.total desc) rn
13 from temp t
14 )
15 select * From rank_them;
CUSTOM PRODUCT_ID RN
------ ---------- ----------
Foot 2 1 --> for Foot, product 2 ranks as the highest
Little 2 1 --> for Little, product 1 ranks as the highest
Little 1 2
SQL>
Moved to a function:
SQL> create or replace function f_product (par_customer_id in order_items.customer_id%type)
2 return products.product_name%type
3 is
4 retval products.product_name%type;
5 begin
6 with
7 temp as
8 (select o.product_id,
9 o.customer_id,
10 sum(o.quantity * o.unit_price) total
11 from order_items o
12 group by o.product_id, o.customer_id
13 ),
14 rank_them as
15 (select t.customer_id,
16 t.product_id,
17 row_number() over (partition by t.customer_id order by t.total desc) rn
18 from temp t
19 )
20 select p.product_name
21 into retval
22 from rank_them r join products p on p.product_id = r.product_id
23 where r.customer_id = par_customer_id
24 and r.rn = 1;
25
26 return retval;
27 end;
28 /
Function created.
SQL>
Testing:
SQL> select f_product ('Little') result from dual;
RESULT
--------------------------------------------------------------------------------
Audi
SQL> select f_product ('Foot') result from dual;
RESULT
--------------------------------------------------------------------------------
Audi
SQL>
Now, you can improve it so that you'd care about no data found issue (when customer didn't buy anything), ties (but you'd then return a collection or a refcursor instead of a scalar value) etc.
[EDIT] I'm sorry, ORDERS table has to be included into the temp CTE; your data model is correct, you don't have to do anything about it - my query was wrong (small screen + late hours issue; not a real excuse, just saying).
So:
with
temp as
(select i.product_id,
o.customer_id,
sum(i.quantity * i.unit_price) total
from order_items i join orders o on o.order_id = i.order_id
group by i.product_id, o.customer_id
),
The rest of my code is - otherwise - unmodified.

Create a product base table by date

I want to create a query that returns number of active products by colander date. I don’t have calendar dim table in database.
Current table
Product_name|prod_id|start_date|end_date
P1|1234|02/01/2020|30/05/2020
P1|2345|02/01/2020|31/12/9999
P1|3456|03/01/2020|31/12/9999
Expected Result
Calander_date|product_name|active_base
01/01/2020|P1|0
02/01/2020|P1|2
03/01/2020|P1|3
01/06/2020|P1|2
Create your own calendar, then - either in the database, as a "real" table (row generator technique helps here), or as a CTE (as I did in the following example):
SQL> with
2 test (product_name, prod_id, start_date, end_date) as
3 -- you have that table; don't type that
4 (select 'P1', 1234, date '2020-01-02', date '2020-05-30' from dual union all
5 select 'P1', 2345, date '2020-01-02', date '9999-12-31' from dual union all
6 select 'P1', 3456, date '2020-01-03', date '9999-12-31' from dual
7 ),
8 calendar (datum) as
9 -- create your own calendar table
10 (select date '2020-01-01' + level - 1
11 from dual
12 connect by level <= 10000 --> number of days you want in calendar
13 )
14 -- final query - join!
15 select c.datum,
16 t.product_name,
17 count(*) active_base
18 from calendar c join test t on c.datum between t.start_date and t.end_date
19 group by c.datum, t.product_name
20 order by c.datum;
DATUM PR ACTIVE_BASE
---------- -- -----------
02/01/2020 P1 2
03/01/2020 P1 3
04/01/2020 P1 3
05/01/2020 P1 3
06/01/2020 P1 3
<snip>
28/05/2020 P1 3
29/05/2020 P1 3
30/05/2020 P1 3
31/05/2020 P1 2
01/06/2020 P1 2
02/06/2020 P1 2
<snip>

Last record saved in a table over one column

I have data:
CLIENT_NO , DATE , TYPE
CLIENT 1- 22/09/2014 -001
CLIENT 1- 19/09/2014 -002
CLIENT 1- 10/09/2014 -005
CLIENT 2- 15/09/2014 -012
CLIENT 2- 20/09/2014 -011
I want to have latest TYPE stored in this table over time for each client. How can I do that using PL/SQL?
Well, you don't really need PL/SQL; pure SQL will do.
One option is to sort them (analytic functions as row_number or rank help in this case), and then fetch rows with row number = 1 (because of order by clause which sorts them by date value in descending order).
SQL> with
2 test (client_no, datum, type) as
3 -- sample data
4 (select 'client 1', date '2014-09-22', '001' from dual union all
5 select 'client 1', date '2014-09-19', '002' from dual union all
6 select 'client 1', date '2014-09-10', '005' from dual union all
7 select 'client 2', date '2014-09-15', '012' from dual union all
8 select 'client 2', date '2014-09-20', '011' from dual
9 ),
10 -- sort them
11 sorted as
12 (select client_no, datum, type,
13 row_number() over (partition by client_no order by datum desc) rn
14 from test
15 )
16 -- select the one with RN = 1
17 select client_no, datum, type
18 from sorted
19 where rn = 1;
CLIENT_NO DATUM TYPE
---------- ---------- ----
client 1 22/09/2014 001
client 2 20/09/2014 011
SQL>

Oracle forms using array error

I am using Oracle Forms and Reports. I am a beginner.
I have a table invoice having fields INVNO,INVDATE.
Invoice table:
Invno Invdate
INVNO1 03/03/2017
Another table passing having these fields :
Passing table
Invno debitcode credit amount sln
INVNO1 debit1 credit1 100 1
INVNO1 debit2 credit2 200 2
INVNO1 debit3 credit3 150 3
INVNO1 debit4 credit4 250 4
Number of records for debit and credit may vary and maximum number of records is 7.
In a form I want to display a horizontal row of record depending on number of records
Invoice No Debit1 Credit1 Amount Debit2 credit2 Amount ..Debit4 Credit4
Forms can't do anything "dynamic", so you'll have to hardcode all 7 credit/debit values, such as this (I've done it up to 3 values). I'd suggest you to create a view based on such a query:
SQL> with passing (invno, debit, credit, amount, sln) as
2 (select 'invno1', 'debit1', 'credit1', 100, 1 from dual union
3 select 'invno1', 'debit2', 'credit2', 200, 2 from dual union
4 select 'invno1', 'debit3', 'credit3', 150, 3 from dual union
5 select 'invno1', 'debit4', 'credit4', 250, 4 from dual)
6 select invno,
7 max(decode(sln, 1, debit)) debit1,
8 max(decode(sln, 1, credit)) credit1,
9 max(decode(sln, 1, amount)) amount1,
10 --
11 max(decode(sln, 2, debit)) debit2,
12 max(decode(sln, 2, credit)) credit2,
13 max(decode(sln, 2, amount)) amount2,
14 --
15 max(decode(sln, 3, debit)) debit3,
16 max(decode(sln, 3, credit)) credit3,
17 max(decode(sln, 3, amount)) amount3
18 from passing
19 group by invno;
INVNO DEBIT1 CREDIT1 AMOUNT1 DEBIT2 CREDIT2 AMOUNT2 DEBIT3 CREDIT3 AMOUNT3
------ ------ ------- ---------- ------ ------- ---------- ------ ------- ----------
invno1 debit1 credit1 100 debit2 credit2 200 debit3 credit3 150
As of Reports: well, the simplest option is to reuse query (or a view, as I've suggested) you used in Forms. Or, if you feel like it, create a matrix report which is capable of doing it dynamically.
P.S. Oh, yes - forgot to ask: title says "error". Which error? You didn't specify it.

split records into buckets based on a sum of counts

i have a table that looks like below. i need to find a way to pick out phone numbers based on a sum of counts (the number will always be different but let's use 130 for this example).
So one of the solutions would be rows 1 through 5 and 11 (if you add up CountOfPeople values from those rows you will get 130). or 1-4,6,7,9,11,12. it doesn't matter which phone numbers are picked, as long as the total is 130.
sometimes you might not be able to get exactly 130, so "as close as possible but not exceeding" would be the rule.
is there a way to do this?
AutoID Phone Number Count Of People
1 5565787 57
2 2342343 30
3 2654456 17
4 3868556 12
5 9856756 12
6 9756456 4
7 4346365 4
8 2376743 3
9 9756343 3
10 2524349 3
11 2029393 2
12 9285656 1
I'm not sure that problem could be solved with pure SQL. But you can use table functions. Here is a little example for your problem.
First of all, we need to create table type:
create type t_bucket_row as object(
phone_number varchar2(10),
count_of_people number,
bucket_no number);
/
create type t_bucket_table as table of t_bucket_row;
/
And table with test data:
create table test_data as
with t as (
select 1 AutoID, '5565787' Phone_Number, 57 Count_Of_People from dual union all
select 2, '2342343', 30 from dual union all
select 3, '2654456', 17 from dual union all
select 4, '3868556', 12 from dual union all
select 5, '9856756', 12 from dual union all
select 6, '9756456', 4 from dual union all
select 7, '4346365', 4 from dual union all
select 8, '2376743', 3 from dual union all
select 9, '9756343', 3 from dual union all
select 10, '2524349', 3 from dual union all
select 11, '2029393', 2 from dual union all
select 12, '9285656', 1 from dual)
select * from t;
Then we create a function that implements an algorithm of distribution of clients (sorry, there is no comments in the code how it works, but it works; I can write it later if you need). Here we create a variable of table type, fill it with phones and bucket numbers, then return it from a function. After that, in SQL query, we use function's result as table in FROM clause. Parameter p_sum is your desired sum of counts of clients:
create or replace function get_buckets(p_sum number) return t_bucket_table is
buckets t_bucket_table := t_bucket_table();
type bucket_sums is table of number index by binary_integer;
sums bucket_sums;
counter number := 0;
found boolean;
begin
sums(1) := 0;
-- next line was edited to fix bug in resuult of distribution:
for i in (select t.*, rownum from test_data t order by t.count_of_people desc) loop
buckets.extend;
counter := counter + 1;
buckets(counter) := t_bucket_row(i.phone_number, i.count_of_people, 0);
if i.count_of_people > p_sum then
continue;
end if;
found := false;
for j in 1..sums.count loop
if sums(j) + i.count_of_people <= p_sum then
sums(j) := sums(j) + i.count_of_people;
buckets(counter).bucket_no := j;
found := true;
exit;
end if;
end loop;
if not found then
sums(sums.count + 1) := i.count_of_people;
buckets(counter).bucket_no := sums.count;
end if;
end loop;
return buckets;
end;
/
Now we can execute this function. Result is:
SQL> select * from table(get_buckets(130));
PHONE_NUMB COUNT_OF_PEOPLE BUCKET_NO
---------- --------------- ----------
5565787 57 1
2342343 30 1
2654456 17 1
3868556 12 1
9856756 12 1
9756456 4 2
4346365 4 2
2376743 3 2
9756343 3 2
2524349 3 2
2029393 2 1
9285656 1 2
12 rows selected.
Buckets distribution:
select bucket_no, sum(count_of_people) from table(get_buckets(130)) group by bucket_no;
BUCKET_NO SUM(COUNT_OF_PEOPLE)
---------- --------------------
1 130
2 18
If count_of_people is more than p_sum, it goes to bucket "0":
SQL> select * from table(get_buckets(35));
PHONE_NUMB COUNT_OF_PEOPLE BUCKET_NO
---------- --------------- ----------
5565787 57 0
2342343 30 1
2654456 17 2
3868556 12 2
9856756 12 3
9756456 4 1
4346365 4 2
2376743 3 3
9756343 3 3
2524349 3 3
2029393 2 2
9285656 1 1
12 rows selected.
SQL> select bucket_no, sum(count_of_people) from table(get_buckets(35)) group by bucket_no;
BUCKET_NO SUM(COUNT_OF_PEOPLE)
---------- --------------------
1 35
2 35
3 21
0 57
You can also try to use User-Defined Aggregate function. Will try to show You in a little example.
First of all, we need to create table types:
create or replace type TTN as table of number;
/
Then we are creating the routines that need to be implemented to define a user-defined aggregate function.
create or replace type TO_BALANCED_BUCKET as object
(
summ TTN,
result int,
static function ODCIAggregateInitialize(sctx in out nocopy TO_BALANCED_BUCKET) return number,
member function ODCIAggregateIterate(self in out nocopy TO_BALANCED_BUCKET, value in number)
return number,
member function ODCIAggregateTerminate(self in TO_BALANCED_BUCKET,
returnValue out number,
flags in number) return number,
member function ODCIAggregateMerge(self in out nocopy TO_BALANCED_BUCKET, ctx2 in TO_BALANCED_BUCKET)
return number
)
/
create or replace type body TO_BALANCED_BUCKET is
static function ODCIAggregateInitialize(sctx in out nocopy TO_BALANCED_BUCKET) return number is
begin
sctx := TO_BALANCED_BUCKET(TTN(0), 1);
return ODCIConst.Success;
end;
member function ODCIAggregateIterate(self in out nocopy TO_BALANCED_BUCKET, value in number)
return number is
b_FoundGroup boolean := false;
begin
if value > 130 then
result := 0;
else
for li in 1..summ.count loop
if summ(li) + value <= 130 then
b_FoundGroup := true;
summ(li) := summ(li) + value;
result := li;
exit;
end if;
end loop;
if not b_FoundGroup then
summ.extend;
summ(summ.count) := value;
result := summ.count;
end if;
end if;
return ODCIConst.Success;
end;
member function ODCIAggregateTerminate(self in TO_BALANCED_BUCKET,
returnValue out number,
flags in number) return number is
begin
returnValue := self.result;
return ODCIConst.Success;
end;
member function ODCIAggregateMerge(self in out nocopy TO_BALANCED_BUCKET, ctx2 in TO_BALANCED_BUCKET)
return number is
begin
return ODCIConst.Error;
end;
end;
/
Then we are creating the aggregate function itself.
create or replace function balanced_bucket(input number) return number
parallel_enable
aggregate using TO_BALANCED_BUCKET;
/
And finally the query itself
with test_data as (
select 1 as AutoID, '5565787' as Phone_Number, 12 as Count_Of_People from dual union all
select 2, '2342343', 3 from dual union all
select 3, '2654456', 1 from dual union all
select 4, '3868556', 12 from dual union all
select 5, '9856756', 4 from dual union all
select 6, '9756456', 4 from dual union all
select 7, '4346365', 57 from dual union all
select 8, '2376743', 3 from dual union all
select 9, '9756343', 3 from dual union all
select 10, '2524349', 30 from dual union all
select 11, '2029393', 2 from dual union all
select 12, '9285656', 17 from dual
)
select t.phone_number, t.count_of_people,
balanced_bucket(t.count_of_people) over(order by t.count_of_people desc) balanced_bucket
from test_data t
Hope this solution will help. The algorithm of distribution of clients is Dmity's.
For the "first bucket" solution this is a nice exercise in recursive subquery factoring. The following query gives you such a bucket (although with phone numbers concatenated to a single string):
with source$ as (
select 1 as AutoID, '5565787' as Phone_Number, 12 as Count_Of_People from dual union all
select 2, '2342343', 3 from dual union all
select 3, '2654456', 1 from dual union all
select 4, '3868556', 12 from dual union all
select 5, '9856756', 4 from dual union all
select 6, '9756456', 4 from dual union all
select 7, '4346365', 57 from dual union all
select 8, '2376743', 3 from dual union all
select 9, '9756343', 3 from dual union all
select 10, '2524349', 30 from dual union all
select 11, '2029393', 2 from dual union all
select 12, '9285656', 17 from dual
),
permutator$ (autoid, phone_number, count_of_people, autoid_list, phone_number_list, count_of_people_sum, count_of_people_list) as (
select S.autoid, phone_number, count_of_people,
to_char(autoid), cast(phone_number as varchar2(4000)), count_of_people, to_char(count_of_people)
from source$ S
union all
select S.autoid, S.phone_number, S.count_of_people,
P.autoid_list||'|'||S.autoid, P.phone_number_list||'|'||S.phone_number, P.count_of_people_sum + S.count_of_people, P.count_of_people_list||'+'||S.count_of_people
from permutator$ P
join source$ S
on S.autoid > P.autoid
where P.count_of_people_sum + S.count_of_people <= 130
)
search depth first by autoid asc set siblings_order$,
priority_ordered$ as (
select P.*,
row_number() over (partition by null order by abs(count_of_people_sum-130), siblings_order$ asc) as your_best_call$
from permutator$ P
)
select autoid_list, phone_number_list, count_of_people_sum, count_of_people_list
from priority_ordered$
where your_best_call$ = 1
;
... and if you rather want a row-by-row list of the original items, then replace the last ...
select autoid_list, phone_number_list, count_of_people_sum, count_of_people_list
from priority_ordered$
where your_best_call$ = 1
;
... with ...
select autoid, count_of_people, phone_number
from priority_ordered$ PO
start with your_best_call$ = 1
connect by PO.autoid_list||'|'||prior PO.autoid = prior PO.autoid_list
;
With a little help from the object-relational features of Oracle the phone number collection may be, in a very elegant way, resolved by a collector object (an object which collects data to its member collection attribute via a member method, returning a new instance of its class). A small example of SQL*Plus spools for this solution:
SQL> set verify off
SQL> define maxcountofpeoplesum = 130
SQL> ##23023283-split-records-into-buckets-based-on-a-sum-of-counts.sql
COUNT_OF_PEOPLE_SUM AUTOID PHONE_NUMBER COUNT_OF_PEOPLE
------------------- ---------- --------------- ---------------
130 1 5565787 12
130 2 2342343 3
130 3 2654456 1
130 5 9856756 4
130 6 9756456 4
130 7 4346365 57
130 10 2524349 30
130 11 2029393 2
130 12 9285656 17
9 rows selected.
SQL> define maxcountofpeoplesum = 15
SQL> ##23023283-split-records-into-buckets-based-on-a-sum-of-counts.sql
COUNT_OF_PEOPLE_SUM AUTOID PHONE_NUMBER COUNT_OF_PEOPLE
------------------- ---------- --------------- ---------------
15 1 5565787 12
15 2 2342343 3
SQL> define maxcountofpeoplesum = 200
SQL> ##23023283-split-records-into-buckets-based-on-a-sum-of-counts.sql
COUNT_OF_PEOPLE_SUM AUTOID PHONE_NUMBER COUNT_OF_PEOPLE
------------------- ---------- --------------- ---------------
148 1 5565787 12
148 2 2342343 3
148 3 2654456 1
148 4 3868556 12
148 5 9856756 4
148 6 9756456 4
148 7 4346365 57
148 8 2376743 3
148 9 9756343 3
148 10 2524349 30
148 11 2029393 2
148 12 9285656 17
12 rows selected.
SQL> define maxcountofpeoplesum = 147
SQL> ##23023283-split-records-into-buckets-based-on-a-sum-of-counts.sql
COUNT_OF_PEOPLE_SUM AUTOID PHONE_NUMBER COUNT_OF_PEOPLE
------------------- ---------- --------------- ---------------
147 1 5565787 12
147 2 2342343 3
147 4 3868556 12
147 5 9856756 4
147 6 9756456 4
147 7 4346365 57
147 8 2376743 3
147 9 9756343 3
147 10 2524349 30
147 11 2029393 2
147 12 9285656 17
11 rows selected.
I'm pretty sure the query could be enhanced to query all buckets, as Dmitry's solution does, but that would result in an even huger and possibly badly performing query. Dmitry's solution looks much simpler and more straightforward for your problem.
Enjoy.

Resources