How I find missing elements in a series using SQL? - sql

I have a table bill with column name bill_id. bill_id value range is from 1 to 40. And I have rows like
bill_id
-----------
1
3
6
8
2
21
34
35
26
40
How can I find the missing elements (4, 5, 7, 9, 10, etc.)?

SQL> create table bill (bill_id)
2 as
3 select 1 from dual union all
4 select 3 from dual union all
5 select 6 from dual union all
6 select 8 from dual union all
7 select 2 from dual union all
8 select 21 from dual union all
9 select 34 from dual union all
10 select 35 from dual union all
11 select 26 from dual union all
12 select 40 from dual
13 /
Table created.
SQL> with all_possible_bill_ids as
2 ( select level bill_id
3 from dual
4 connect by level <= 40
5 )
6 select bill_id
7 from all_possible_bill_ids
8 minus
9 select bill_id
10 from bill
11 /
BILL_ID
----------
4
5
7
9
10
11
12
13
14
15
16
17
18
19
20
22
23
24
25
27
28
29
30
31
32
33
36
37
38
39
30 rows selected.
Regards,
Rob.

Related

Query to create multiple equal records of volume distribution using duration (in terms of months)

Input Output ResultsHope you are doing good.
I am stuck in a requirement where I need to have records distributed into multiple records based on the duration I get it from a linking table.
Suppose I have a volume of 100 and duration I am getting is 20 months linking table then my output should have 20 records of each 5(100/20). Could you please help me with the query how to do this SQL.
The WITH clause is here just to generate some sample data and, as such, it is not a part of the answer.
You can join the tables ON PRODUCT columns, limit the iterations using LEVEL <= DURATION, group the data and show the amount either as Min, Max or Avg of COST/DURATION rounded to two decimals. I put all of the data in the select list. Here is the complete code with the result. Regards...
WITH
t_duration AS
(
Select 'A' "PRODUCT", 10 "DURATION" From Dual Union All
Select 'B' "PRODUCT", 6 "DURATION" From Dual Union All
Select 'C' "PRODUCT", 4 "DURATION" From Dual
),
t_cost AS
(
Select 'A' "PRODUCT", 100 "COST" From Dual Union All
Select 'B' "PRODUCT", 50 "COST" From Dual Union All
Select 'C' "PRODUCT", 40 "COST" From Dual
)
SELECT
LEVEL "MONTH_ORDER_NUMBER",
d.PRODUCT "PRODUCT",
d.DURATION "DURATION",
c.COST "COST",
Round(Avg(c.COST / d.DURATION), 2) "AVG_MONTHLY_AMOUNT",
Round(Max(c.COST / d.DURATION), 2) "MAX_MONTHLY_AMOUNT",
Round(Min(c.COST / d.DURATION), 2) "MIN_MONTHLY_AMOUNT"
FROM
t_duration d
INNER JOIN
t_cost c ON(c.PRODUCT = d.PRODUCT)
CONNECT BY LEVEL <= d.DURATION
GROUP BY
d.PRODUCT, d.DURATION, c.COST, LEVEL
ORDER BY
d.PRODUCT, LEVEL
--
-- R e s u l t
--
-- MONTH_ORDER_NUMBER PRODUCT DURATION COST AVG_MONTHLY_AMOUNT MAX_MONTHLY_AMOUNT MIN_MONTHLY_AMOUNT
-- ------------------ ------- ---------- ---------- ------------------ ------------------ ------------------
-- 1 A 10 100 10 10 10
-- 2 A 10 100 10 10 10
-- 3 A 10 100 10 10 10
-- 4 A 10 100 10 10 10
-- 5 A 10 100 10 10 10
-- 6 A 10 100 10 10 10
-- 7 A 10 100 10 10 10
-- 8 A 10 100 10 10 10
-- 9 A 10 100 10 10 10
-- 10 A 10 100 10 10 10
-- 1 B 6 50 8.33 8.33 8.33
-- 2 B 6 50 8.33 8.33 8.33
-- 3 B 6 50 8.33 8.33 8.33
-- 4 B 6 50 8.33 8.33 8.33
-- 5 B 6 50 8.33 8.33 8.33
-- 6 B 6 50 8.33 8.33 8.33
-- 1 C 4 40 10 10 10
-- 2 C 4 40 10 10 10
-- 3 C 4 40 10 10 10
-- 4 C 4 40 10 10 10
That looks as if ntile would do the job (at least, that's how I understood the question).
Here's a table with 100 rows (that's your "volume of 100").
SQL> create table test (id) as
2 select level from dual connect by level <= 100;
Table created.
You'd then pass 20 (that's "duration of 20 months") to ntile and get the result - see the grp column, having 20 groups, each of them having 5 rows:
SQL> select id, ntile(20) over (order by id) grp
2 from test
3 order by id;
ID GRP
---------- ----------
1 1
2 1
3 1
4 1
5 1
6 2
7 2
8 2
9 2
10 2
11 3
12 3
13 3
14 3
15 3
<snip>
91 19
92 19
93 19
94 19
95 19
96 20
97 20
98 20
99 20
100 20
100 rows selected.
SQL>
[EDIT, based on new information]
With sample tables you posted:
SQL> with
2 duration (product, duration) as
3 (select 'A', 10 from dual union all
4 select 'B', 6 from dual union all
5 select 'C', 4 from dual
6 ),
7 cost (product, cost) as
8 (select 'A', 100 from dual union all
9 select 'B', 50 from dual union all
10 select 'C', 40 from dual
11 )
query would look like this:
12 select d.product,
13 c.cost / d.duration as amount
14 from duration d join cost c on c.product = d.product
15 cross join table(cast(multiset(select level from dual
16 connect by level <= d.duration
17 ) as sys.odcinumberlist))
18 order by d.product;
PRODUCT AMOUNT
---------- ----------
A 10
A 10
A 10
A 10
A 10
A 10
A 10
A 10
A 10
A 10
B 8,33333333
B 8,33333333
B 8,33333333
B 8,33333333
B 8,33333333
B 8,33333333
C 10
C 10
C 10
C 10
20 rows selected.
SQL>

Selecting only the highest value in Oracle SQL [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Select First Row of Every Group in sql [duplicate]
(2 answers)
Return row with the max value of one column per group [duplicate]
(3 answers)
SQL: getting the max value of one column and the corresponding other columns [duplicate]
(2 answers)
Closed 12 months ago.
I have a query where I might get only one or possibly multiple records for each CONTROLNBR + LINENBR combo -
SELECT ACCTNBR, BOOKDATE, CONTROLNBR, LINENBR, TRANDETSEQ,
ING1, ING1AMT, ING2, ING2AMT, ING3, ING3AMT, ING4, ING4AMT
FROM TABLE1 WHERE COSTCENTER = '9999' ORDER BY CONTROLNBR, LINENBR,
TRANDETSEQ
Some data returned might be -
12345 01-FEB-19 3771 1 5 R4 13 Y1 41 Y3 1
12345 01-FEB-19 3771 1 11 R4 21 Y1 41 Y3 9
12345 01-FEB-19 3771 1 12 R4 21 Y1 41 Y3 17
12345 01-FEB-19 3771 1 18 R4 21 Y1 61 Y3 45
12345 01-FEB-19 3772 3 5 R2 3 Y3 38 L1 8
12345 01-FEB-19 3773 1 5 N1 8 W1 2 L1 42
12345 01-FEB-19 3773 1 12 N1 10 W1 4 L1 45
24568 01-FEB-19 3786 2 4 L1 28 G2 40 N1 13
24568 01-FEB-19 3786 2 8 L1 28 G2 45 N1 18
24568 01-FEB-19 3786 2 12 L1 28 G2 48 N1 18
In each case, I would just need the line with the highest TRANDETSEQ number for each CONTROLNBR + LINENBR combo. So I would only want -
12345 01-FEB-19 3771 1 18 R4 21 Y1 61 Y3 45
12345 01-FEB-19 3772 3 5 R2 3 Y3 38 L1 8
12345 01-FEB-19 3773 1 12 N1 10 W1 4 L1 45
24568 01-FEB-19 3786 2 12 L1 28 G2 48 N1 18
Is there some function or something like LAST or MAX that might work here?
Rank them, and then fetch row(s) that rank as the highest.
Sample data:
SQL> with test (acctnbr, controlnbr, linenbr, trandatseq) as
2 (select 12345, 3771, 1, 5 from dual union all
3 select 12345, 3771, 1, 11 from dual union all
4 select 12345, 3771, 1, 12 from dual union all
5 select 12345, 3771, 1, 18 from dual union all
6 --
7 select 12345, 3772, 3, 5 from dual union all
8 --
9 select 12345, 3773, 1, 5 from dual union all
10 select 12345, 3773, 1, 12 from dual union all
11 --
12 select 24568, 3786, 2, 4 from dual union all
13 select 24568, 3786, 2, 12 from dual
14 ),
Query begins here:
15 temp as
16 -- sort them
17 (select t.*,
18 rank() over (partition by acctnbr, controlnbr , linenbr order by trandatseq desc) rnk
19 from test t
20 )
21 -- finally, fetch rows that rank as "highest"
22 select acctnbr, controlnbr, linenbr, trandatseq
23 from temp
24 where rnk = 1;
ACCTNBR CONTROLNBR LINENBR TRANDATSEQ
---------- ---------- ---------- ----------
12345 3771 1 18
12345 3772 3 5
12345 3773 1 12
24568 3786 2 12
SQL>

How to do calculate recursive sum in SQL in configurable in Big Query

I have query problem, I'm using Google BigQuery (Just to give you context if it is different). I need two value which is Value_A and Value_B. Value_A is top X value, and Value_B is the rest of top X value.Here's my Input Table
Date Value
20 10
19 10
18 10
17 10
16 10
15 10
14 10
13 10
12 10
11 10
10 10
9 10
8 10
7 10
6 10
5 10
4 10
3 10
2 10
1 10
In this case, the value of X is 6, but I need to be configurable.
In date 20, Value_A is sum from the 6 top data in Value (which is date 14 to 20), and Value_B is sum of rest of top data (which is date 14 an below).
In date 19, Value_A is sum from the 6 top data in Value (which is date 13 to 19), and Value_B is sum of rest of top data (which is date 13 an below).
Here's my output
Date Value Value_A Value_B
20 10 60 140
19 10 60 130
18 10 60 120
17 10 60 110
16 10 60 100
15 10 60 90
14 10 60 80
13 10 60 70
12 10 60 60
11 10 60 50
10 10 60 40
9 10 60 30
8 10 60 20
7 10 60 10
6 10 60 0
5 10 50 0
4 10 40 0
3 10 30 0
2 10 20 0
1 10 10 0
Below is for BigQuery Standard SQL
#standardSQL
SELECT date, value,
SUM(value) OVER(ORDER BY date DESC ROWS BETWEEN CURRENT ROW AND 5 FOLLOWING) Value_A,
IFNULL(SUM(value) OVER(ORDER BY date DESC ROWS BETWEEN 6 FOLLOWING AND UNBOUNDED FOLLOWING), 0) Value_B
FROM `project.dataset.table`
-- ORDER BY date DESC
If to apply to sample data from your question, as in below example
#standardSQL
WITH `project.dataset.table` AS (
SELECT 20 date, 10 value UNION ALL
SELECT 19, 10 UNION ALL
SELECT 18, 10 UNION ALL
SELECT 17, 10 UNION ALL
SELECT 16, 10 UNION ALL
SELECT 15, 10 UNION ALL
SELECT 14, 10 UNION ALL
SELECT 13, 10 UNION ALL
SELECT 12, 10 UNION ALL
SELECT 11, 10 UNION ALL
SELECT 10, 10 UNION ALL
SELECT 9, 10 UNION ALL
SELECT 8, 10 UNION ALL
SELECT 7, 10 UNION ALL
SELECT 6, 10 UNION ALL
SELECT 5, 10 UNION ALL
SELECT 4, 10 UNION ALL
SELECT 3, 10 UNION ALL
SELECT 2, 10 UNION ALL
SELECT 1, 10
)
SELECT date, value,
SUM(value) OVER(ORDER BY date DESC ROWS BETWEEN CURRENT ROW AND 5 FOLLOWING) Value_A,
IFNULL(SUM(value) OVER(ORDER BY date DESC ROWS BETWEEN 6 FOLLOWING AND UNBOUNDED FOLLOWING), 0) Value_B
FROM `project.dataset.table`
ORDER BY date DESC
result is
Row date value Value_A Value_B
1 20 10 60 140
2 19 10 60 130
3 18 10 60 120
4 17 10 60 110
5 16 10 60 100
6 15 10 60 90
7 14 10 60 80
8 13 10 60 70
9 12 10 60 60
10 11 10 60 50
11 10 10 60 40
12 9 10 60 30
13 8 10 60 20
14 7 10 60 10
15 6 10 60 0
16 5 10 50 0
17 4 10 40 0
18 3 10 30 0
19 2 10 20 0
20 1 10 10 0

Insert number of records based on field value in Oracle

I have the following script :-
SELECT
quoteid,
tariff_length,
cost
FROM
tblquotesnew q
LEFT JOIN
tbltariffsnew t
ON q.tariff_id = t.tariff
which may return something like:-
quoteid tariff_length cost
310 4 12
311 6 16
Is it possible to INSERT rows into a seperate table, where the number of rows inserted is based tariff_length?
So, using the above, the insertion table (tblcommnew) would look like
commid quoteid cost
1 310 12
2 310 12
3 310 12
4 310 12
5 311 16
6 311 16
7 311 16
8 311 16
9 311 16
10 311 16
Here's one option:
SQL> with test (quoteid, tariff_length, cost) as
2 (select 310, 4, 12 from dual union
3 select 311, 6, 16 from dual
4 )
5 select rownum as commid, quoteid, cost
6 from test,
7 table(cast(multiset(select level from dual
8 connect by level <= tariff_length
9 ) as sys.odcinumberlist));
COMMID QUOTEID COST
---------- ---------- ----------
1 310 12
2 310 12
3 310 12
4 310 12
5 311 16
6 311 16
7 311 16
8 311 16
9 311 16
10 311 16
10 rows selected.
SQL>
A slight variation on #Littlefoot's approach is to use an XMLTable to generate the combinations:
with tblquotesnew (quoteid, tariff_length, cost) as (
select 310, 4, 12 from dual
union all select 311, 6, 16 from dual
)
select rownum as commid, quoteid, cost
from tblquotesnew
cross join xmltable ('1 to xs:integer($n)' passing tariff_length as "n");
COMMID QUOTEID COST
---------- ---------- ----------
1 310 12
2 310 12
3 310 12
4 310 12
5 311 16
6 311 16
7 311 16
8 311 16
9 311 16
10 311 16
As an insert you then just do:
insert into tblcommnew (commid, quoteid, cost)
select rownum, quoteid, cost
from tblquotesnew
cross join xmltable ('1 to xs:integer($n)' passing tariff_length as "n");
10 rows inserted.

Select sum of condition

I have a table like below. I want to select some rows that sum of cnt<120. how to do this?
cnt id
_________
6 14001
17 14005
14 14017
16 14024
9 14025
7 14027
10 14029
14 14048
23 14055
18 14056
19 14058
18 14059
18 14063
15 14064
9 14086
17 14095
9 14098
14 14116
10 14138
8 14147
17 14165
22 14171
22 14191
18 14194
13 14204
17 14221
13 14245
14 14249
6 14254
17 14257
9 14260
19 14261
26 14263
6 14264
27 14265
19 14269
11 14287
SELECT SUM(cnt)
FROM my_table
WHERE cnt < 120
You can try this:-
SELECT *
FROM test O
HAVING (SELECT sum(cnt) FROM test WHERE cnt <= O.cnt) <120