ORACLE: Select row, if most recent value of column equals X - oracle

I'll preemptively say I search the other questions and online, however could not find it. Whether that be poor wording choice on my part, or otherwise.
I have a ticketing queue and searching through it's history. Column "Group" will contain the group name that owns the ticket upon a transfer action. There are many other actions, such as just adding comments to the ticket. I am only interested if the most recent value in the column (ignoring nulls) if it is "QUEUE_X".
I know this is improper because of the group-function but this is logically how I view it:
select
*
from
ticket_history
where
max(group) = 'QUEUE_X'
order by
date
;
I don't want to strictly do where group = 'X' because tickets can be transferred around a lot and I only care about the tickets in my queue at the current time. Also, I cannot find if or where the original developer put a CURRENT_QUEUE-like column, so out of luck the easy way. Don't take it too literal, just wanted to illustrate the null values in GROUP and I'd like the most recent value to equal Queue_X
Example table data of a ticket I would NOT want (though if the query occured at 12:12, I would have):
STRT_DATE | PERSON | GROUP | TICKET_NUM | END_DATE
12/3/15 12:00 | Ted | Queue_Y | 111111 | 12/3/15 12:05
12/3/15 12:05 | Bob | | 111111 | 12/3/15 12:10
12/3/15 12:10 | Ted | Queue_Y | 222222 | 12/3/15 12:45
12/3/15 12:10 | Bob | Queue_X | 111111 | 12/3/15 12:15
12/3/15 12:15 | Bob | | 222222 | 12/3/15 12:45
12/3/15 12:15 | Mary | Queue_Y | 111111 | 12/3/15 12:25
12/3/15 12:25 | Ted | | 222222 | 12/3/15 12:35
12/3/15 12:35 | Ted | | 333333 | 12/3/15 12:45
12/3/15 12:35 | Bob | Queue_X | 333333 | 12/3/15 12:45
12/3/15 12:45 | Ted | | 111111 | 12/3/15 12:55
12/3/15 12:55 | Bob | Queue_X | 111111 | 12/3/15 12:56
Desired Results:
12/3/15 12:35 | Bob | Queue_X | 333333 | 12/3/15 12:45
12/3/15 12:55 | Bob | Queue_X | 111111 | 12/3/15 12:56
(Still in Queue_Y):
12/3/15 12:10 | Ted | Queue_Y | 222222 | 12/3/15 12:45
Thank you in advance.
EDIT: Added more tickets and an additional member to Queue_Y (so there is not a 1-1 relationship between queue and person)

not totally sure what you are trying to accomplish but I think you want something like dense_rank() here is one I put together mytable represents your data, t1 does the dense rank check
ok here is the query running after 12:15 and no data is returned.
WITH mytable
AS (SELECT TO_DATE ('12/3/15 12:00', 'mm/dd/yy hh:mi') AS strt_dt,
'Ted' AS person,
'Queue_Y' AS grp,
123456 AS ticket_num
FROM DUAL
UNION ALL
SELECT TO_DATE ('12/3/15 12:05', 'mm/dd/yy hh:mi') AS strt_dt,
'Bob',
NULL,
123456
FROM DUAL
UNION ALL
SELECT TO_DATE ('12/3/15 12:10', 'mm/dd/yy hh:mi') AS strt_dt,
'Bob',
'Queue_X',
123456
FROM DUAL
UNION ALL
SELECT TO_DATE ('12/3/15 12:15', 'mm/dd/yy hh:mi') AS strt_dt,
'Ted',
'Queue_Y',
123456
FROM DUAL),
t2
AS (SELECT mytable.*,
DENSE_RANK ()
OVER (PARTITION BY ticket_num ORDER BY strt_dt DESC)
AS test_for_Q
FROM mytable)
SELECT strt_dt, person, grp, ticket_num
FROM t2
WHERE ticket_num IN (SELECT ticket_num
FROM t2
WHERE test_for_Q = 1 AND Grp = 'Queue_X')
now here is the query running at 12:12 and you get data back.
WITH mytable
AS (SELECT TO_DATE ('12/3/15 12:00', 'mm/dd/yy hh:mi') AS strt_dt,
'Ted' AS person,
'Queue_Y' AS grp,
123456 AS ticket_num
FROM DUAL
UNION ALL
SELECT TO_DATE ('12/3/15 12:05', 'mm/dd/yy hh:mi') AS strt_dt,
'Bob',
NULL,
123456
FROM DUAL
UNION ALL
SELECT TO_DATE ('12/3/15 12:10', 'mm/dd/yy hh:mi') AS strt_dt,
'Bob',
'Queue_X',
123456
FROM DUAL
-- UNION ALL
-- SELECT TO_DATE ('12/3/15 12:15', 'mm/dd/yy hh:mi') AS strt_dt,
-- 'Ted',
-- 'Queue_Y',
-- 123456
-- FROM DUAL
) ,
t2
AS (SELECT mytable.*,
DENSE_RANK ()
OVER (PARTITION BY ticket_num ORDER BY strt_dt DESC)
AS test_for_Q
FROM mytable)
SELECT strt_dt, person, grp, ticket_num
FROM t2
WHERE ticket_num IN (SELECT ticket_num
FROM t2
WHERE test_for_Q = 1 AND Grp = 'Queue_X')
results
STRT_DT,PERSON,GRP,TICKET_NUM
12/3/2015 12:10:00 PM,Bob,Queue_X,123456
12/3/2015 12:05:00 PM,Bob,,123456
12/3/2015 12:00:00 PM,Ted,Queue_Y,123456

If I understand your problem right your query would be:
select a.*
from ticket_history a
INNER JOIN ( select PERSON, GROUP, max(STRT_DATE) as maxdt
from ticket_history
where group = 'Queue_X'
group by PERSON, GROUP ) b
ON ( a.person = b.person
and a.group = b.group
and a.STRT_DATE = b.maxdt )
order by a.STRT_DATE desc
Please see if it is what you need, otherwise let me know.

Try this (edited):
select strt_date, person, group, ticket_num, end_date
from ticket_history
where (rowid, end_date) IN (select rowid, max(end_date)
from ticket_history
where group = 'QUEUE_X'
group by rowid)
order by end_date;
This will reflect what you show in your desired output.

Related

Oracle sql set end date based on previous start date

I have one table where I need to add new column endDate for future implementation but since we have currently only start date for all records I need to set endDate which should be equal to start date from previous record that are connected by userId and if it is only one record for that user than end date will have some value in future.
For example:
Table structure:
ID | USER_ID | START_DATE | END_DATE
-------------------------------------
1 | 1 | 01.01.2015 |
2 | 1 | 01.01.2016 |
3 | 1 | 01.07.2018 |
4 | 1 | 01.08.2021 |
5 | 2 | 01.01.2015 |
6 | 3 | 01.01.2016 |
7 | 3 | 01.07.2018 |
8 | 4 | 01.08.2021 |
Expected result should be like this
ID | USER_ID | START_DATE | END_DATE
-------------------------------------
1 | 1 | 01.01.2015 | 01.01.2016
2 | 1 | 01.01.2016 | 01.07.2018
3 | 1 | 01.07.2018 | 01.08.2021
4 | 1 | 01.08.2021 | 01.01.2050
5 | 2 | 01.01.2015 | 01.01.2050
6 | 3 | 01.01.2016 | 01.07.2018
7 | 3 | 01.07.2018 | 01.01.2050
8 | 4 | 01.08.2021 | 01.01.2050
Can someone help me with how query in oracle databse should look to update it like this?
I've tried something with for loop but not sure how to continue from this step
DECLARE
CURSOR c_contract
IS
SELECT
USER_ID
FROM
CONTRACT
ORDER_BY START_DATE

BEGIN
FOR r_contract IN c_contract
LOOP

dbms_output.put_line( r_contract.USER_ID );
END LOOP;

END;
Use the LEAD analytic function with the default date as the third argument:
SELECT t.*,
LEAD( start_date, 1, DATE '2050-01-01') OVER (
PARTITION BY user_id
ORDER BY start_date
) AS end_date
FROM table_name t
Which, for the sample data:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
CREATE TABLE table_name ( ID, USER_ID, START_DATE ) AS
SELECT 1, 1, DATE '2015-01-01' FROM DUAL UNION ALL
SELECT 2, 1, DATE '2016-01-01' FROM DUAL UNION ALL
SELECT 3, 1, DATE '2018-07-01' FROM DUAL UNION ALL
SELECT 4, 1, DATE '2021-08-01' FROM DUAL UNION ALL
SELECT 5, 2, DATE '2015-01-01' FROM DUAL UNION ALL
SELECT 6, 3, DATE '2016-01-01' FROM DUAL UNION ALL
SELECT 7, 3, DATE '2018-07-01' FROM DUAL UNION ALL
SELECT 8, 4, DATE '2021-08-01' FROM DUAL;
Outputs:
ID
USER_ID
START_DATE
END_DATE
1
1
2015-01-01 00:00:00
2016-01-01 00:00:00
2
1
2016-01-01 00:00:00
2018-07-01 00:00:00
3
1
2018-07-01 00:00:00
2021-08-01 00:00:00
4
1
2021-08-01 00:00:00
2050-01-01 00:00:00
5
2
2015-01-01 00:00:00
2050-01-01 00:00:00
6
3
2016-01-01 00:00:00
2018-07-01 00:00:00
7
3
2018-07-01 00:00:00
2050-01-01 00:00:00
8
4
2021-08-01 00:00:00
2050-01-01 00:00:00
If you want to add a column then:
ALTER TABLE table_name ADD (end_date DATE);
MERGE INTO table_name dst
USING (
SELECT ROWID AS rid,
LEAD( start_date, 1, DATE '2050-01-01') OVER (
PARTITION BY user_id
ORDER BY start_date
) AS end_date
FROM table_name
) src
ON (dst.ROWID = src.rid)
WHEN MATCHED THEN
UPDATE SET end_date = src.end_date;
fiddle

Vertica - Join on MAX(Date)

I m trying to replicate this sql syntax in Vertica, but it returns "ERROR: Subqueries in the ON clause are not supported".
The aim is to join two tables, table1 and table2, on column and date, if a.date = b.date or the closest but lesser b.date.
Any hint?
SELECT *
FROM table1 a
LEFT JOIN table2 b
ON a.column = b.column
AND b.Date = (SELECT MAX (b2.Date)
FROM table2 b2
WHERE a.column = b2.column
AND b2.Date <= a.Date)
Vertica has something handier for that: the event series join. With that, you can OUTER JOIN two tables so that they match the same or the immediately preceding value of the join column of the other table. The predicate is INTERPOLATE PREVIOUS VALUE instead of an equi-predicate.
Here's an example with the oil pressure curve and the engine rpm curve with the timestamp matching only once.
DROP TABLE IF EXISTS oilpressure;
CREATE TABLE oilpressure (
op_vid,op_ts,op_psi
) AS (
SELECT 42,TIMESTAMP '2020-04-01 07:00:00', 25.356
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:12', 35.124
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:23', 47.056
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:34', 45.225
)
;
DROP TABLE IF EXISTS revspeed;
CREATE TABLE revspeed (
rs_vid,rs_ts,rpm
) AS (
SELECT 42,TIMESTAMP '2020-04-01 07:00:00', 2201
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:10', 3508
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:20', 6504
UNION ALL SELECT 42,TIMESTAMP '2020-04-01 07:00:30', 6608
)
;
-- without
\pset null '(null)'
SELECT *
FROM oilpressure
LEFT OUTER JOIN revspeed
ON op_vid=rs_vid AND op_ts=rs_ts ;
-- out Null display is "(null)".
-- out op_vid | op_ts | op_psi | rs_vid | rs_ts | rpm
-- out --------+---------------------+--------+--------+---------------------+--------
-- out 42 | 2020-04-01 07:00:00 | 25.356 | 42 | 2020-04-01 07:00:00 | 2201
-- out 42 | 2020-04-01 07:00:12 | 35.124 | (null) | (null) | (null)
-- out 42 | 2020-04-01 07:00:23 | 47.056 | (null) | (null) | (null)
-- out 42 | 2020-04-01 07:00:34 | 45.225 | (null) | (null) | (null)
-- with
SELECT *
FROM oilpressure
LEFT OUTER JOIN revspeed
ON op_vid=rs_vid AND rs_ts INTERPOLATE PREVIOUS VALUE op_ts;
-- out op_vid | op_ts | op_psi | rs_vid | rs_ts | rpm
-- out --------+---------------------+--------+--------+---------------------+------
-- out 42 | 2020-04-01 07:00:00 | 25.356 | 42 | 2020-04-01 07:00:00 | 2201
-- out 42 | 2020-04-01 07:00:12 | 35.124 | 42 | 2020-04-01 07:00:10 | 3508
-- out 42 | 2020-04-01 07:00:23 | 47.056 | 42 | 2020-04-01 07:00:20 | 6504
-- out 42 | 2020-04-01 07:00:34 | 45.225 | 42 | 2020-04-01 07:00:30 | 6608

how to check length of "concatenate" string

Has anybody an idea how to write a query in Oracle to get length of characters:
| user | action |
-------------------------
| mary | aaa | # 3 characters from action
| mary | bbbbb | # 5 characters from action
| mary | c | # 1 character from action
| adam | xx | # 2 characters from action
| adam | yyyy | # 4 characters from action
| adam | zzzzzzz | # 7 characters from action
So in result should be sum of characters for each:
| mary | 9 |
| adam | 13 |
Thanks.
SUM + LENGTH along with GROUP BY user. Sample data in lines #1 - 14; query you need begins at line #15.
SQL> WITH
2 test (cuser, action)
3 AS
4 (SELECT 'mary', 'aaa' FROM DUAL
5 UNION ALL
6 SELECT 'mary', 'bbbbb' FROM DUAL
7 UNION ALL
8 SELECT 'mary', 'c' FROM DUAL
9 UNION ALL
10 SELECT 'adam', 'xx' FROM DUAL
11 UNION ALL
12 SELECT 'adam', 'yyyy' FROM DUAL
13 UNION ALL
14 SELECT 'adam', 'zzzzzzz' FROM DUAL)
15 SELECT cuser, SUM (LENGTH (action))
16 FROM test
17 GROUP BY cuser;
CUSE SUM(LENGTH(ACTION))
---- -------------------
mary 9
adam 13
SQL>
Use the LENGTH function in your aggregation:
SELECT "USER",
SUM( LENGTH( action ) ) AS total_length
FROM table_name
GROUP BY "USER"
Which, for your sample data:
CREATE TABLE table_name( "USER", action ) AS
SELECT 'mary', 'aaa' FROM DUAL UNION ALL
SELECT 'mary', 'bbbbb' FROM DUAL UNION ALL
SELECT 'mary', 'c' FROM DUAL UNION ALL
SELECT 'adam', 'xx' FROM DUAL UNION ALL
SELECT 'adam', 'yyyy' FROM DUAL UNION ALL
SELECT 'adam', 'zzzzzzz' FROM DUAL;
Outputs:
USER | TOTAL_LENGTH
:--- | -----------:
mary | 9
adam | 13
db<>fiddle here

Select SUM with GROUP BY by day interval

I have this table in Oracle Database:
date | column1 | column2
01/05/2020 00:00 | 50 | 20
01/05/2020 00:15 | 60 | 30
01/05/2020 00:30 | 70 | 40
... | |
01/05/2020 23:45 | 80 | 50
02/05/2020 00:00 | 100 | 40
02/05/2020 00:15 | 110 | 35
And I have this SELECT script:
SELECT
period,
liquid
FROM
(
SELECT
( nvl(SUM(m.column1), 0) - nvl(SUM(m.column2), 0) ) liquid,
TO_CHAR(trunc(m.date), 'dd/MM/YYYY') period
FROM
table m
WHERE
m.id_register IN (id_register)
AND m.date BETWEEN TO_DATE('01/05/2020 00:15:00', 'dd/mm/yyyy hh24:mi:ss') AND TO_DATE('01/06/2020 00:00:00', 'dd/mm/yyyy hh24:mi:ss')
GROUP BY
TO_CHAR(trunc(m.date), 'dd/MM/YYYY')
) vrdm
I need to sum (difference column1 and column2) all day registers in interval between 00:15 and 00:00 (next day) per day. How to make it?
The result that I need is like this:
period | liquid
01/05/2020 | 3000 -> SUM(all differences between column1 and column2 between 01/05 00:15 and 02/05 00:00)
02/05/2020 | 4000 -> SUM(all differences between column1 and column2 between 02/05 00:15 and 03/05 00:00)
03/05/2020 | 3500 -> SUM(all differences between column1 and column2 between 03/05 00:15 and 04/05 00:00)
Just check time part in where clause:
SELECT to_char(trunc(m.dt), 'dd/MM/YYYY') period,
nvl(SUM(m.column1), 0) - nvl(SUM(m.column2), 0) liquid
FROM m
WHERE to_char(m.dt, 'hh24:mi') >= '00:15'
GROUP BY trunc(m.dt)
dbfiddle

Create Oracle View with columns from row

these are my two tables:
table1:
ID | Description
0 | test
1 | test1
table2:
ID | YEAR | VALUE
0 | 2019 | 1000
0 | 2020 | 2000
1 | 2019 | 3000
1 | 2020 | 4000
I would like to have this view (2019 and 2020 could be custom rows):
ID | Description | 2019 | 2020
0 | test | 1000 | 2000
1 | test1 | 3000 | 4000
I know how to join those two tables but not how to add the values from table2 to that custom column. Thanks
Use a join combined with aggregation/pivoting:
CREATE VIEW yourView AS
SELECT
t1.ID,
t1.Description,
MAX(CASE WHEN t2.YEAR = 2019 THEN t2.VALUE END) AS "2019",
MAX(CASE WHEN t2.YEAR = 2020 THEN t2.VALUE END) AS "2020"
FROM table1 t1
INNER JOIN table2 t2
ON t1.ID = t2.ID
GROUP BY
t1.ID,
t1.Description
ORDER BY
t1.ID;
Demo
Use below View:
Create View View_name
as
SELECT
tbl1.ID,
tbl1.Description,
MAX(CASE WHEN tbl2.YEAR = 2019 THEN tbl2.VALUE END) AS "2019",
MAX(CASE WHEN tbl2.YEAR = 2020 THEN tbl2.VALUE END) AS "2020"
FROM table1 tbl1
INNER JOIN table2 tbl2
ON tbl1.ID = tbl2.ID
GROUP BY
tbl1.ID,
tbl1.Description;

Resources