Oracle Sql query to count time span with certain criteria - oracle

Oracle Sql query , I was trying to count the grand total for time difference that is greater than 2, but when I tried this it just counted all the rows from the query instead of just the rows that have the criteria I was looking for. Anybody have an idea of what I am missing or a better approach . Thanks
This is my query
select DC.CUST_FIRST_NAME,DC.CUST_LAST_NAME,oi.customer_id,oi.order_timestamp,oi.order_timestamp - LAG(oi.order_timestamp) OVER (ORDER BY oi.order_timestamp) AS "Difference(In Days)" ,
(select Count('Elapsed Order Difference')
from demo_orders oi,
demo_customers dc
where OI.CUSTOMER_ID = DC.CUSTOMER_ID
group by 'Elapsed Order Difference'
having count('Elapsed Order Difference') > 3
)Total
from demo_orders oi,
demo_customers dc
where OI.CUSTOMER_ID = DC.CUSTOMER_ID
Results
CUST_FIRST_NAME CUST_LAST_NAME CUSTOMER_ID ORDER_TIMESTAMP Difference(In Days) TOTAL
Eugene Bradley 7 8/14/2013 5:59:11 PM 10
William Hartsfield 2 8/28/2013 5:59:11 PM 14 10
Edward "Butch" OHare 4 9/8/2013 5:59:11 PM 11 10
Edward Logan 3 9/10/2013 5:59:11 PM 2 10
Edward Logan 3 9/20/2013 5:59:11 PM 10 10
Albert Lambert 6 9/25/2013 5:59:11 PM 5 10
Fiorello LaGuardia 5 9/30/2013 5:59:11 PM 5 10
William Hartsfield 2 10/8/2013 5:59:11 PM 8 10
John Dulles 1 10/14/2013 5:59:11 PM 6 10
Eugene Bradley 7 10/17/2013 5:59:11 PM 3 10

This is untested, but I think it might give you what you're after.
with raw_data as (
select
dc.cust_first_name, dc.cust_last_name,
oi.customer_id, oi.order_timestamp,
oi.order_timestamp - LAG(oi.order_timestamp) OVER
(ORDER BY oi.order_timestamp) AS "Difference(In Days)",
case
when oi.order_timestamp - LAG(oi.order_timestamp)
over (ORDER BY oi.order_timestamp) > 2 then 1
else 0
end as gt2
from
demo_orders oi,
demo_customers dc
where
oi.customer_id = dc.customer_id
)
select
cust_first_name, cust_last_name,
customer_id, order_timestamp,
"Difference(In Days)",
sum (gt2) over (partition by 1) as total
from raw_data
When you do Count('Elapsed Order Difference') above, you are counting every row, no matter what. You could have put count ('frog') or count (*) and have gotten the same result. The having count > 3 was already satisfied since the count of all rows was 10.
In general, I'd try to avoid using a scalar for a field in a query as you have in your example. I'm not saying it's never a good idea, but I would argue that there is usually a better way to do it. With 10 rows, you'll hardly notice a performance difference, but as your datasets grow, this can create issues.
Expected output:
fn ln id order date dif total
E B 7 8/14/2014 8
W H 2 8/28/2014 14 8
E O 4 9/8/2014 11 8
E L 3 9/10/2014 2 8
E L 3 9/20/2014 10 8
A L 6 9/25/2014 5 8
F L 5 9/30/2014 5 8
W H 2 10/8/2014 8 8
J D 1 10/14/2014 6 8
E B 7 10/17/2014 3 8

Related

Finding tuples if it only exists in all occurrences of a constraint

Database (all entries are integers):
ID | BUDGET
1 | 20
8 | 20
10 | 20
5 | 4
9 | 4
10 | 4
1 | 11
9 | 11
Suppose my constraint is having a budget of >= 10.
I would want to return ID of 1 only in this case. How do I go about it?
I've tried taking the cross product of itself after selecting budget >= 10 and returning if id1 = id2 and budget1 <> budget2 but that does not work in the case where there's only 1 budget that is >= 10. (EG below)
ID | BUDGET
1 | 20
8 | 20
10 | 20
1 | 4
5 | 4
9 | 4
10 | 4
9 | 4
If I were to do what I did for the first example, nothing will be returned as budget1 <> budget2 will result in an empty table.
EDIT1: I can only use relational algebra to solve the problem. So SQL's exist, where and count keywords cant be used.
Edit2: Only project, select, rename, set difference, set union, left join, right join, full inner join, natural joins, set intersection and cross product allowed
The question is not completely clear to me. If you want to return all the ID for which there is a budget greater than 10, and no budget less than 10, the expression is simply the following:
π(ID)(σ(BUDGET>=10)(R)) - π(ID)(σ(BUDGET<10)(R))
If, an the other hand, you want all the ID which have all the budgets present in the relation and greater then 10, then we must use the ÷ operator:
R ÷ π(BUDGET)(σ(BUDGET>=10)(R))
From your comment, the second case is the correct one. Let’s see how to compute the division from its definition (applied to two generic relations R(A) and S(B)):
R ÷ S = πA-B(R) - πA-B((πA-B(R) x S) - R)
where R is the original relation, and
S = π(BUDGET)(σ(BUDGET>=10)(R)),
that is:
BUDGET
------
20
11
Starting from the inner expression:
πA-B(R) is equal to πID(R) =
ID
--
1
5
8
9
10
then πA-B(R) x S) is:
ID BUDGET
---------
1 20
1 11
5 20
5 11
8 20
8 11
9 20
9 11
10 20
10 11
then ((πA-B(R) x S) - R) is:
ID BUDGET
---------
5 20
5 11
8 11
9 20
10 20
then πA-B((πA-B(R) x S) - R) is:
ID
__
5
8
9
10
and, finally, subtracting this relation from πA-B(R) we obtain the result:
ID
--
1

Sum values by date range in multiple columns

I need to sum value by date range in multiple columns. Every date range is one week of a month. It can be shorter than 7 days if it is the start of the month or the end of the month.
For example, I have dates for February:
my_user my_date my_value
A 01.02.2019 100
A 02.02.2019 150
B 01.02.2019 90
Z 28.02.2019 120
How can I have in date range format such as below?
my_user 01/02-03/02 04/02-10/02 11/02-17/02 18/02-24/02 25/02-28/02
A 250 0 0 0 0
B 90 0 0 0 0
Z 0 0 0 0 120
Any suggestions? Thanks!
You can do this:
select *
from (
select to_char(dt, 'iw') - to_char(trunc(dt, 'month'), 'iw') + 1 wk, usr, val from t)
pivot (sum(val) for wk in (1, 2, 3, 4, 5, 6))
demo
USR 1 2 3 4 5 6
--- ---------- ---------- ---------- ---------- ---------- ----------
A 250
B 90
Z 120
Header numbers are the weeks of month. Maximum may be 6 if month starts at the end of the week and is longer than 28 days.
Similiar way you can find first and last day of each week if needed, but you can't put them as headers, or at least not easily.
Edit:
it is possible to define certain date range with pivot, simple as two
dates? For example, I need to sum values from 5 December 2018 to 4
January 2019, 5 January 2019 to 4 February 2019, 5 March 2019 to 4
April 2019
Yes. Everything depends on how we count first and next weeks. Here:
to_char(dt, 'iw') - to_char(trunc(dt, 'month'), 'iw') + 1
i am subtracting week in year for given date and week in year of first day in month for this date. You can simply replace this second value with your starting date, either by hardcoding it in your query or by sending parameter to query or finding minimum date at first in a subquery:
(to_char(dt, 'iw') - to_char(date '2019-03-05', 'iw')) + 1
or
(to_char(dt, 'iw') - to_char((select min(dt) from data), 'iw')) + 1
Edit 2:
There is one problem however. When user defined period contains two or more years. to_date(..., 'iw') works fine for one year, but for two we get values 51, 52, 01, 02... We have to deal with this somehow, for instance like here:
with t(dt1, dt2) as (select date '2018-12-16', date '2019-01-15' from dual)
select min(dt) mnd, max(dt) mxd, iw, row_number() over (order by min(dt)) rn
from (select dt1 + level - 1 dt, to_char(dt1 + level - 1, 'iw') iw
from t connect by level -1 <= dt2 - dt1)
group by iw
which gives us:
MND MXD IW RN
----------- ----------- -- ----------
2018-12-16 2018-12-16 50 1
2018-12-17 2018-12-23 51 2
2018-12-24 2018-12-30 52 3
2018-12-31 2019-01-06 01 4
2019-01-07 2019-01-13 02 5
2019-01-14 2019-01-15 03 6
In first line we have user defined date ranges. Then I did hierarchical query looping through all dates in range assigning week, then grouped by this week, found start and end dates for each week and assigned row number rn which can be further used by pivot.
You can now simply join your input data with this query, let's name it weeks:
from data join weeks on dt between mnd and mxd
and make pivot. But for longer periods you have to find how many weeks there can be and specify them in pivot clause in (1, 2, 3, 4...). You can also add aliases if you need:
pivot ... for rn in (1 week01, 2 week02... 12 week12)
There is no simply way to avoid listing them manually. If you need it please look for oracle dynamic pivot in SO, there where hundreds similiar questions already. ;-)

Hive - add column with number of distinct values in groups

Suppose I have the following data.
number group
1 a
1 a
3 a
4 a
4 a
5 c
6 b
6 b
6 b
7 b
8 b
9 b
10 b
14 b
15 b
I would like to group the data by group and add a further column which say how many distinct values of number each group has.
My desired output would look as follows:
number group dist_number
1 a 3
1 a 3
3 a 3
4 a 3
4 a 3
5 c 1
6 b 9
6 b 9
6 b 9
7 b 9
8 b 9
9 b 9
10 b 9
14 b 9
15 b 9
What I have tried is:
> select *, count(distinct number) over(partition by group) from numbers;
11 11
As one sees, this aggregates globally and calculates the number of distinct values independently from the group.
One thing I could do is to use group by as follows:
hive> select *, count(distinct number) from numbers group by group;
a 3
b 7
c 1
And then join over group
But I thought maybe there is a more easy solution to this, e.g., using the over(partition by group) method?
You definitely want to use windowing functions here. I'm not exactly sure how you got 11 11 from the query your tried; I'm 99% sure if you try to count(distinct _) in Hive with an over/partition it will complain. To get around this you can use collect_set() to get an array of the distinct elements in the partition and then you can use size() to count the elements.
Query:
select *
, size(num_arr) dist_num
from (
select *
, collect_set(num) over (partition by grp) num_arr
from db.tbl ) x
Output:
4 a [4,3,1] 3
4 a [4,3,1] 3
3 a [4,3,1] 3
1 a [4,3,1] 3
1 a [4,3,1] 3
15 b [15,14,10,9,8,7,6] 7
14 b [15,14,10,9,8,7,6] 7
10 b [15,14,10,9,8,7,6] 7
9 b [15,14,10,9,8,7,6] 7
8 b [15,14,10,9,8,7,6] 7
7 b [15,14,10,9,8,7,6] 7
6 b [15,14,10,9,8,7,6] 7
6 b [15,14,10,9,8,7,6] 7
6 b [15,14,10,9,8,7,6] 7
5 c [5] 1
I included in the arrays in the output so you could see what was happening, obviously you can discard them in your query. As as note, doing a self-join here is really a disaster with regards to performance (and it's more lines of code).
As per your requirement,this may work:
select number,group1,COUNT(group1) OVER (PARTITION BY group1) as dist_num from table1;

How to sort individual rows in descending order?

I have a table which looks like the following:
Name LastName tPoints aPoints sPoints gPoints type
John Johnny 15 14 13 10 1
Joe P. 12 11 26 10 1
Matt Q. 11 26 37 44 2
Sorine P. 55 9 8 7 2
Ali Ahmed 30 44 88 65 2
... ... .. .. .. .. 3
3
I would like to sort INDIVIDUAL ROWS and display based on TYPE
NOTE: i can't use order by in oracle because it sorts only 1 row and the others
is sorted based on the first row
I don't want to break the table apart into individual tables, then sort it, and then update it back to the original table.
so, the output will looks something like this, for tPoints - i need to display all
15 - John Johnny
12 - Joe P.
and for aPoints
44 - Ali Ahmed
26 - Matt Q.
9 - Sorine P.
and so on ...
in a nutshell, if type = 1 then sort tPoints in descending, if type = 2 then sort aPoints, if type = 3 then sort sPoints, and so on....
what would be an efficient way to chive this?
Regards,
For the sake of simplicity this example includes only two types. Add as many types as you need.
SQL> with t1(Name1, LastName, tPoints, aPoints, sPoints, gPoints, type1) as(
2 select 'John' , 'Johnny', 15, 14, 13, 10, 1 from dual union all
3 select 'Joe' , 'P.' , 12, 11, 26, 10, 1 from dual union all
4 select 'Matt' , 'Q.' , 11, 26, 37, 44, 2 from dual union all
5 select 'Sorine', 'P.' , 55, 9 , 8 , 7, 2 from dual union all
6 select 'Ali' , 'Ahmed' , 30, 44, 88, 65, 2 from dual
7 )
8 select type1
9 , tpoints
10 , apoints
11 , name1
12 , Lastname
13 from t1
14 order by case when type1=1 then tpoints else type1 end desc,
15 case when type1=2 then apoints else type1 end desc;
TYPE1 TPOINTS APOINTS NAME1 LASTNAME
---------- ---------- ---------- ------ --------
1 15 14 John Johnny
1 12 11 Joe P.
2 30 44 Ali Ahmed
2 11 26 Matt Q.
2 55 9 Sorine P.

Oracle7:Merge data are same in many records to one record

I would like to merge data are same in many records to one record.
From
FO LINE FLOOR COLOR SUM
S4714EH02 EH 11F AK 9
S4714EH02 EH 11F AK 18
S4714EH02 EH 11F FE 9
S4714EH02 EH 11F FE 18
S4714EH02 EH 12F AK 9
S4714EH02 EH 12F AK 18
S4714EH02 EH 12F FE 9
S4714EH02 EH 12F FE 18
To
FO LINE FLOOR COLOR SUM
S4714EH02 EH 11F AK 9,18
S4714EH02 EH 11F FE 9,18
S4714EH02 EH 12F AK 9,18
S4714EH02 EH 12F FE 9,18
I know it can in sql server 2008 but I don't know it can make in oracle7 .
Please help me. Thank you.
Oracle 7 is a fine release of that database. it introduced many new features, it performed well and obviously it remains exceedingly stable. But it is long in the tooth and lacks many features available to us in more recent versions of the product.
For instance, all of the normal techniques we can use to aggregate values into a list only work in 9i or higher. (Some may work in 8i, my mind is a little fuzzy here as it's been almost a decade since I worked with Oracle that old)
So do you have any options in Oracle 7? The only one I can think of is to run a stored procedure as part of a reporting pre-process. This stored procedure would loop round the rows you want to query, assemble rows which matched your desired output and then insert them into a different table. This table would then service the actual query.
This is an extremely clunky workaround, and may not be viable in your situation. But alas that is the cost of using legacy software.
As already very well said by APC, this version is really old and lacks all kinds of functions to do string aggregation. I have worked with version 7 in the previous millenium though, and I think the next sequence should work in Oracle7. I could be wrong though, but obviously I can't check it.
SQL> create table t (fo,line,floor,color,sum)
2 as
3 select 'S4714EH02', 'EH', '11F', 'AK', 9 from dual union all
4 select 'S4714EH02', 'EH', '11F', 'AK', 18 from dual union all
5 select 'S4714EH02', 'EH', '11F', 'FE', 9 from dual union all
6 select 'S4714EH02', 'EH', '11F', 'FE', 18 from dual union all
7 select 'S4714EH02', 'EH', '12F', 'AK', 9 from dual union all
8 select 'S4714EH02', 'EH', '12F', 'AK', 18 from dual union all
9 select 'S4714EH02', 'EH', '12F', 'FE', 9 from dual union all
10 select 'S4714EH02', 'EH', '12F', 'FE', 18 from dual
11 /
Table created.
SQL> create function f
2 ( p_fo in t.fo%type
3 , p_line in t.line%type
4 , p_floor in t.floor%type
5 , p_color in t.color%type
6 ) return varchar2
7 is
8 cursor c
9 is
10 select t.sum
11 from t
12 where t.fo = p_fo
13 and t.line = p_line
14 and t.floor = p_floor
15 and t.color = p_color
16 order by t.sum
17 ;
18 l_concatenated_sum varchar2(2000);
19 begin
20 for r in c
21 loop
22 l_concatenated_sum := l_concatenated_sum || ',' || to_char(r.sum);
23 end loop;
24 return substr(l_concatenated_sum,2);
25 end f;
26 /
Function created.
SQL> select fo
2 , line
3 , floor
4 , color
5 , f(fo,line,floor,color) sum
6 from t
7 group by fo
8 , line
9 , floor
10 , color
11 /
FO LI FLO CO SUM
--------- -- --- -- --------------------
S4714EH02 EH 11F AK 9,18
S4714EH02 EH 11F FE 9,18
S4714EH02 EH 12F AK 9,18
S4714EH02 EH 12F FE 9,18
4 rows selected.
Regards,
Rob.
In the special case where you have only two records per distinct key -- as shown by your sample data -- you could do this:
SELECT fo, line, floor, color, MIN(sum) || ',' || MAX(sum)
FROM theTable
GROUP BY fo, line, floor, color;
But this can't be generalized to handle more than two values of sum per key.

Resources