Below is an example table.
DECLARE #Temp TABLE (ID int, Name varchar(50), LiveDate Date, LiveTime time(7), Duration_Seconds int)
INSERT INTO #Temp (ID, Name, LiveDate, LiveTime, Duration)
SELECT 1, 'ABC', '2013-08-19', '00:01:00.0000000', 300
UNION ALL
SELECT 2, 'ABC', '2013-08-19', '00:01:00.0000000', 300
UNION ALL
SELECT 3, 'DEF', '2013-08-19', '00:01:00.0000000', 300
UNION ALL
SELECT 4, 'DEF', '2013-08-19', '00:03:00.0000000', 300
UNION ALL
SELECT 5, 'GHI', '2013-08-19', '00:01:00.0000000', 300
UNION ALL
SELECT 6, 'GHI', '2013-08-19', '00:01:00.0000000', 300
UNION ALL
SELECT 7, 'GHI', '2013-08-19', '00:03:00.0000000', 300
UNION ALL
SELECT 8, 'GHI', '2013-08-19', '00:09:00.0000000', 300
UNION ALL
SELECT 9, 'GHI', '2013-08-20', '00:06:00.0000000', 300
UNION ALL
SELECT 10, 'JKL', '2013-08-19', '00:01:00.0000000', 300
UNION ALL
SELECT 11, 'MNO', '2013-08-19', '00:01:00.0000000', 300
SELECT *,
CASE
WHEN COUNT(*) OVER (PARTITION BY Name, LiveDate, LiveTime) > 1 THEN 1
ELSE 0
END AS Duplicate
FROM #Temp
Now, the output that I desire is the following.
/*
Desired Output
ID Name LiveDate Livetime Duration_Seconds Duplicate OverLap
1 ABC 2013-08-19 00:01:00.0000000 300 Yes No
2 ABC 2013-08-19 00:01:00.0000000 300 Yes No
3 DEF 2013-08-19 00:01:00.0000000 300 No Yes
4 DEF 2013-08-19 00:03:00.0000000 300 No Yes
5 GHI 2013-08-19 00:01:00.0000000 300 Yes Yes
6 GHI 2013-08-19 00:01:00.0000000 300 Yes Yes
7 GHI 2013-08-19 00:03:00.0000000 300 No Yes
8 GHI 2013-08-19 00:09:00.0000000 300 No No
9 GHI 2013-08-20 00:06:00.0000000 300 No No
10 JKL 2013-08-19 00:01:00.0000000 300 No No
11 MNO 2013-08-19 00:01:00.0000000 300 No No
*/
How may I go about doing this? Any help would be appreciated.
I am unsure of how to find Overlap.
For Overlap to be Yes/True/1, the Name and Date has to be the same.
Then, we have to look at the time and duration.
Let's say for GHI, time = 12:01 for ID 5 and 6 and 12:03 for ID 7.
But according to the duration, which is 300 seconds OR 5 mins, since 12:03 is within 5 mins from 12:01, I want to mark Overlap = Yes/True/1 for those three records.
Consider LiveTime as Start Time. Duration_Seconds as total time the record was Live.
So GHI ID 5 & 6 LiveTime = 12:01 AM and lasted for 300 seconds (5 minutes). So It went live at 12:01 AM and was dead at 12:06 AM.
GHI ID 7 went live at 12:03 AM with SAME Name and Date. But it should not have since we already have record Live from 12:01 AM to 12:06 AM with same Name and Date. Therefore, all GHI are marked as Overlap = Yes/True/1
Hope this helps you understand what I am trying to do.
THX
This will work, there might be a simpler way:
;WITH cte AS ( SELECT *,
CASE
WHEN COUNT(*) OVER (PARTITION BY Name, LiveDate, LiveTime) > 1 THEN 1
ELSE 0
END AS Duplicate
FROM #Temp)
SELECT DISTINCT a.*,CASE WHEN b.ID IS NOT NULL THEN 1 ELSE 0 END 'Overlap'
FROM cte a
LEFT JOIN cte b
ON a.NAME = b.NAME
AND a.LiveDate = b.LiveDate
AND ((b.LiveTime > a.Livetime AND b.LiveTime < DATEADD(SECOND,a.Duration_Seconds,a.LiveTime))
OR (a.LiveTime > b.Livetime AND a.LiveTime < DATEADD(SECOND,b.Duration_Seconds,b.LiveTime)))
You might have to adjust the JOIN criteria if the above doesn't work for all instances of overlap.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 11 months ago.
Improve this question
I have an Oracle DB Connection that has data (SELECT * FROM SALES) as in the picture, i want a query that gives me which 3 consecutive days are those who have the sum of PREMIUM_TOTAL > 100.
I have tried with the method lead, lag , DATADIFF but failed. Also i'm new at this, if you can give me hints please.
If you want 3 rows from successive days then you can use a recursive query:
WITH successive_days (day, products, total, depth) AS (
SELECT entry_date,
TO_CHAR(product_id),
premium_total,
1
FROM table_name
UNION ALL
SELECT s.day + 1,
s.products || ',' || t.product_id,
s.total + t.premium_total,
s.depth + 1
FROM successive_days s
INNER JOIN table_name t
ON (s.day + 1 = t.entry_date)
WHERE s.depth < 3
)
SELECT day AS final_day, products, total
FROM successive_days
WHERE depth = 3
AND total >= 100;
Which, for the sample data:
CREATE TABLE table_name (product_id, entry_date, premium_total) AS
SELECT 1, DATE '2022-03-01', 1 FROM DUAL UNION ALL
SELECT 2, DATE '2022-03-01', 20 FROM DUAL UNION ALL
SELECT 4, DATE '2022-03-02', 30 FROM DUAL UNION ALL
SELECT 5, DATE '2022-03-03', 30 FROM DUAL UNION ALL
SELECT 10, DATE '2022-03-21', 12 FROM DUAL UNION ALL
SELECT 11, DATE '2022-03-31', 40.5 FROM DUAL UNION ALL
SELECT 13, DATE '2022-03-05', 70 FROM DUAL UNION ALL
SELECT 12, DATE '2022-03-05', 80 FROM DUAL UNION ALL
SELECT 14, DATE '2022-03-05', 10 FROM DUAL UNION ALL
SELECT 20, DATE '2022-03-06', 20 FROM DUAL UNION ALL
SELECT 21, DATE '2022-03-07', 30 FROM DUAL UNION ALL
SELECT 22, DATE '2022-03-07', 40 FROM DUAL UNION ALL
SELECT 30, DATE '2022-03-08', 20 FROM DUAL UNION ALL
SELECT 31, DATE '2022-03-09', 50 FROM DUAL UNION ALL
SELECT 40, DATE '2022-03-10', 2 FROM DUAL;
Outputs:
FINAL_DAY
PRODUCTS
TOTAL
2022-03-07 00:00:00
13,20,21
120
2022-03-07 00:00:00
13,20,22
130
2022-03-07 00:00:00
12,20,21
130
2022-03-07 00:00:00
12,20,22
140
2022-03-09 00:00:00
21,30,31
100
2022-03-09 00:00:00
22,30,31
110
If you want all the rows (at least 3) that are all within 3 successive days then you can use MATCH_RECOGNIZE:
SELECT MIN(entry_date) AS start_day,
MAX(entry_date) AS final_day,
LISTAGG(product_id, ',') WITHIN GROUP (ORDER BY entry_date) AS products,
SUM(premium_total) AS total
FROM table_name
MATCH_RECOGNIZE(
ORDER BY entry_date
MEASURES
MATCH_NUMBER() AS mno
ALL ROWS PER MATCH
AFTER MATCH SKIP TO NEXT ROW
PATTERN (first_day+ second_day+ third_day* final_day)
DEFINE
first_day AS FIRST(entry_date) = entry_date,
second_day AS FIRST(entry_date) + 1 = entry_date,
third_day AS FIRST(entry_date) + 2 = entry_date,
final_day AS FIRST(entry_date) + 2 = entry_date
AND SUM(premium_total) >= 100
)
GROUP BY mno;
Which, for the sample data, outputs:
START_DAY
FINAL_DAY
PRODUCTS
TOTAL
2022-03-05 00:00:00
2022-03-07 00:00:00
12,13,14,20,21,22
250
2022-03-05 00:00:00
2022-03-07 00:00:00
13,14,20,21,22
170
2022-03-05 00:00:00
2022-03-07 00:00:00
13,20,21,22
160
2022-03-06 00:00:00
2022-03-08 00:00:00
20,21,22,30
110
2022-03-07 00:00:00
2022-03-09 00:00:00
21,22,30,31
140
2022-03-07 00:00:00
2022-03-09 00:00:00
22,30,31
110
db<>fiddle here
I have two tables that I am trying to merge together to create a joined output of them both.
Here is how my tables are structured:
Table 1:
date
type_s
calls
declines
09-SEP-21
insurance
500
600
09-SEP-21
roadside
66
60
09-SEP-21
AAA
34
700
09-SEP-21
retail
1
650
Table 2:
date
type_s
cnt
09-SEP-21
insurance
5
09-SEP-21
AAA
3
09-SEP-21
retail
79
How do I get my output to be like this:
date
type_s
calls
declines
cnt
09-SEP-21
insurance
500
600
5
09-SEP-21
roadside
66
60
0
09-SEP-21
AAA
34
700
3
09-SEP-21
retail
1
650
79
Notice how in table 2, roadside is not present because there is no data for that type. I am trying to merge them together to keep all records, even if data in column 'type_s' from table 1 is not present in table 2. In that case, just put 0 as the cnt for that type.
here is my code so far:
with t2 as
(select DATE_TIME , type_s, calls, declines
from table_1
),
CD as
(
SELECT
DATE_TIME, type_s, CNT
FROM table2
),
DD AS(
SELECT TO_DATE(current_date - 1) AS chart_date,
type_s,
calls,
declines
FROM t2),
GG As
(
SELECT
DD.chart_date ,
DD.type_s,
DD.calls,
DD.declines,
CD.CNT
FROM CD,DD
WHERE CD.type_s= DD.type_s)
select CHART_DATE,type_s,calls,declines,cnt
from GG;
My code runs fine but it doesnt give me the output I desire (view below). It excludes the roadside row from table 1.
date
type_s
calls
declines
cnt
09-SEP-21
insurance
500
600
5
09-SEP-21
AAA
34
700
3
09-SEP-21
retail
1
650
79
Any ideas or suggestions as to how to fix my code in order to create my desired output? Any help would be highly appreciated.
Looks like an outer join with a NVL function. Sample data in lines #1 - 12, query begins at line #13.
SQL> with
2 table1 (datum, type_s, calls, declines) as
3 (select date '2021-09-09', 'insurance', 500, 600 from dual union all
4 select date '2021-09-09', 'roadside' , 66, 60 from dual union all
5 select date '2021-09-09', 'AAA' , 34, 700 from dual union all
6 select date '2021-09-09', 'retail' , 1, 650 from dual
7 ),
8 table2 (datum, type_s, cnt) as
9 (select date '2021-09-09', 'insurance', 5 from dual union all
10 select date '2021-09-09', 'AAA' , 3 from dual union all
11 select date '2021-09-09', 'retail' , 79 from dual
12 )
13 select a.datum, a.type_s, a.calls, a.declines, nvl(b.cnt, 0) cnt
14 from table1 a left join table2 b on a.datum = b.datum
15 and a.type_s = b.type_s
16 /
DATUM TYPE_S CALLS DECLINES CNT
---------- --------- ---------- ---------- ----------
2021-09-09 insurance 500 600 5
2021-09-09 AAA 34 700 3
2021-09-09 retail 1 650 79
2021-09-09 roadside 66 60 0
SQL>
Basically I have Product table like this:
date price
--------- -----
02-SEP-14 50
03-SEP-14 60
04-SEP-14 60
05-SEP-14 60
07-SEP-14 71
08-SEP-14 45
09-SEP-14 45
10-SEP-14 24
11-SEP-14 60
I need to update the table in this form
date price id
--------- ----- --
02-SEP-14 50 1
03-SEP-14 60 2
04-SEP-14 60 2
05-SEP-14 60 2
07-SEP-14 71 3
08-SEP-14 45 4
09-SEP-14 45 4
10-SEP-14 24 5
11-SEP-14 60 6
What I have tried:
CREATE SEQUENCE user_id_seq
START WITH 1
INCREMENT BY 1
CACHE 20;
ALTER TABLE Product
ADD (ID number);
UPDATE Product SET ID = user_id_seq.nextval;
This is updating the ID in the usual way like 1,2,3,4,5..
I have no idea how to do it using basic SQL commands. Please suggest how can I make it. Thank you in advance.
Here is one way to create a view from your base data. I assume you have more than one product (identified by product id), and that the price dates aren't necessarily consecutive. The sequence is separate for each product id. (Also, product should be the name of a different table - where the product id is primary key, and you have other information such as product name, category, etc. The table in your post would be more properly called something like price_history.)
alter session set nls_date_format='dd-MON-rr';
create table product ( prod_id number, dt date, price number );
insert into product ( prod_id, dt, price )
select 101, '02-SEP-14', 50 from dual union all
select 101, '03-SEP-14', 60 from dual union all
select 101, '04-SEP-14', 60 from dual union all
select 101, '05-SEP-14', 60 from dual union all
select 101, '07-SEP-14', 71 from dual union all
select 101, '08-SEP-14', 45 from dual union all
select 101, '09-SEP-14', 45 from dual union all
select 101, '10-SEP-14', 24 from dual union all
select 101, '11-SEP-14', 60 from dual union all
select 102, '02-SEP-14', 45 from dual union all
select 102, '04-SEP-14', 45 from dual union all
select 102, '05-SEP-14', 60 from dual union all
select 102, '06-SEP-14', 50 from dual union all
select 102, '09-SEP-14', 60 from dual
;
commit;
create view product_vw ( prod_id, dt, price, seq ) as
select prod_id, dt, price,
count(flag) over (partition by prod_id order by dt)
from ( select prod_id, dt, price,
case when price = lag(price) over (partition by prod_id order by dt)
then null else 1 end as flag
from product
)
;
Now check what the view looks like:
select * from product_vw;
PROD_ID DT PRICE SEQ
------- ------------------- ---------- ----------
101 02/09/0014 00:00:00 50 1
101 03/09/0014 00:00:00 60 2
101 04/09/0014 00:00:00 60 2
101 05/09/0014 00:00:00 60 2
101 07/09/0014 00:00:00 71 3
101 08/09/0014 00:00:00 45 4
101 09/09/0014 00:00:00 45 4
101 10/09/0014 00:00:00 24 5
101 11/09/0014 00:00:00 60 6
102 02/09/0014 00:00:00 45 1
102 04/09/0014 00:00:00 45 1
102 05/09/0014 00:00:00 60 2
102 06/09/0014 00:00:00 50 3
102 09/09/0014 00:00:00 60 4
NOTE: This answers the question that was originally asked. The OP changed the data.
If your data is not too large, you can use a correlated subquery:
update product p
set id = (select count(distinct p2.price)
from product p2
where p2.date <= p.date
);
If your data is larger, then merge is more appropriate.
WITH cts AS
(
SELECT row_number() over (partition by price order by price ) as id
,date
,price
FROM Product
)
UPDATE p
set p.id = cts.id
from product p join cts on cts.id = p.id
This is the best way by which you try to do.
There is no another simple way to do this using simple statements
i would like to compute the data from a table where it consists date, hour and data ..there are a lot of data inside the table.. the hour is int datatype in the database and other (DATE.etc) are datatype varchar.. the example are as below:
DATE hour data1 data2
-------------------------------
01/01/2010 1 10860 1234
01/01/2010 2 10861 1234
01/01/2010 3 10862 1234
01/01/2010 4 10863 567
01/01/2010 5 10864 458
02/01/2010 1 10865 3467
02/01/2010 2 10866 7890
02/01/2010 3 10867 863
02/01/2010 4 10868 0
02/01/2010 5 10868 698
03/01/2010 1 10868 4693
03/01/2010 2 10868 7853
03/01/2010 3 10868 5987
....................etc
and from above data. i would like to sum the data from date: 01/01/2010, hour: 2 to the next day date:02/01/2010, hour:1 and of course date:02/01/2010, hour:2 to date:03/01/2010, hour:1 and the rest of the data .. meaning that the output of the data result would be as below:
DATE sdata1 sdata2
-------------------------------
01/01/2010 54315 6960
02/01/2010 54337 14144
03/01/2010 21736 13840
...................etc
the datatype for date, data1 and data2 are varchar EXCEPT time is datatype int...
the sdata1 and sdata2 are the sum of the data and is there any other way that may sum up those data in condition from today hour:2 to the next day hour:2 ? thank you so much everyone ..thank you ..
those data are resulted from bulk insert of PLC data...
here is my own solution but it never work out!!
select SUM(CONVERT(int,data1)) AS sdata1,SUM(CONVERT(int,data2)) AS sdata2 from table where
(CONVERT(datetime, date, 105) >= CONVERT(datetime,date,105) and CONVERT(int,hour) >= 2) and
(CONVERT(datetime, date, 105) <= DATEADD(day, 1,CONVERT(datetime,date,105)) and CONVERT(int,hour) < 2)
and
month(CONVERT(datetime,Date,103))= '01' and year(CONVERT(datetime,Date,103))= '2010'
someone help me figure this out please ... my brain is burst ... ##
Here you go
set dateformat dmy
declare #t table(DATE date, hour int, data1 int, data2 int)
insert into #t
select '01/01/2010', 1, 10860, 1234 union all
select '01/01/2010', 2, 10861, 1234 union all
select '01/01/2010', 3, 10862, 1234 union all
select '01/01/2010', 4, 10863, 567 union all
select '01/01/2010', 5, 10864, 458 union all
select '02/01/2010', 1, 10865, 3467 union all
select '02/01/2010', 2, 10866, 7890 union all
select '02/01/2010', 3, 10867, 863 union all
select '02/01/2010', 4, 10868, 0 union all
select '02/01/2010', 5, 10868, 698 union all
select '03/01/2010', 1, 10868, 4693 union all
select '03/01/2010', 2, 10868, 7853 union all
select '03/01/2010', 3, 10868, 5987
select dateadd(day,case when hour>1 then 1 else 0 end,date),
sum(data1),sum(data2)
from #t
group by dateadd(day,case when hour>1 then 1 else 0 end,date)
the desire result should be like this
DATE sdata1 sdata2
01/01/2010 54315 6960
02/01/2010 54337 14144
03/01/2010 21736 13840
...................etc
but from your query .it works well...thank you and just the end result is as below:
DATE sdata1 sdata2
02/01/2010 54315 6960
03/01/2010 54337 14144
04/01/2010 21736 13840
...................etc
meaning that the query sum up the data from 01/01/2010 until 02/01/2010 and output for 02/01/2010..hehe ..
Thanks for taking the time to examine my issue.
I'm trying to figure out a way to return dates when an account reaches 0
Sample data:
DATE ACCOUNT AMOUNT
11/01 001 100
11/02 002 50
11/03 001 -100
11/07 001 20
11/15 002 -50
11/20 001 -20
Wanted results:
Account ZeroDate
001 11/03
002 11/15
001 11/20
So far I haven't been able to figure out anything that works. Might you be able to point me in the right direction?
Thanks again in advance!
You can use analytic functions to compute the running balance
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select date '2011-11-01' dt, 1 account, 100 amt from dual union all
3 select date '2011-11-02', 2, 50 from dual union all
4 select date '2011-11-03', 1, -100 from dual union all
5 select date '2011-11-07', 1, 20 from dual union all
6 select date '2011-11-15', 2, -50 from dual union all
7 select date '2011-11-20', 1, -20 from dual
8 )
9 select dt,
10 account,
11 amt,
12 sum(amt) over (partition by account order by dt) current_balance
13* from x
SQL> /
DT ACCOUNT AMT CURRENT_BALANCE
--------- ---------- ---------- ---------------
01-NOV-11 1 100 100
03-NOV-11 1 -100 0
07-NOV-11 1 20 20
20-NOV-11 1 -20 0
02-NOV-11 2 50 50
15-NOV-11 2 -50 0
6 rows selected.
and then use the running balance to find the zero dates.
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select date '2011-11-01' dt, 1 account, 100 amt from dual union all
3 select date '2011-11-02', 2, 50 from dual union all
4 select date '2011-11-03', 1, -100 from dual union all
5 select date '2011-11-07', 1, 20 from dual union all
6 select date '2011-11-15', 2, -50 from dual union all
7 select date '2011-11-20', 1, -20 from dual
8 )
9 select account,
10 dt zero_date
11 from (
12 select dt,
13 account,
14 amt,
15 sum(amt) over (partition by account order by dt) current_balance
16 from x
17 )
18* where current_balance = 0
SQL> /
ACCOUNT ZERO_DATE
---------- ---------
1 03-NOV-11
1 20-NOV-11
2 15-NOV-11
create table myacct (dt varchar2(5)
, account varchar2(3)
, amount number
)
;
insert into myacct values ('11/01', '001', 100);
insert into myacct values ('11/02', '002', 50);
insert into myacct values ('11/03', '001', -100);
insert into myacct values ('11/07', '001', 20);
insert into myacct values ('11/15', '002', -50);
insert into myacct values ('11/20', '001', -20);
commit;
/* results wanted:
Account ZeroDate
001 11/03
002 11/15
001 11/20 */
select account "Account", dt "ZeroDate"
from myacct
where amount <= 0
;
/* results from above query:
Account ZeroDate
001 11/03
002 11/15
001 11/20
*/