Datetime SQL statement (Working in SQL Developer) - sql

I'm new to the SQL scene but I've started to gather some data that makes sense to me after learning a little about SQL Developer. Although, I do need help with a query.
My goal:
To use the current criteria I have and select records only when the date-time value is within 5 minutes of the latest date-time. Here is my current sql statement
`SELECT ABAMS.T_WORKORDER_HIST.LINE_NO AS Line,
ABAMS.T_WORKORDER_HIST.STATE AS State,
ASMBLYTST.V_SEQ_SERIAL_ALL.BUILD_DATE,
ASMBLYTST.V_SEQ_SERIAL_ALL.SEQ_NO,
ASMBLYTST.V_SEQ_SERIAL_ALL.SEQ_NO_EXT,
ASMBLYTST.V_SEQ_SERIAL_ALL.UPD_REASON_CODE,
ABAMS.V_SERIAL_LINESET.LINESET_DATE AS "Lineset Time",
ABAMS.T_WORKORDER_HIST.SERIAL_NO AS ESN,
ABAMS.T_WORKORDER_HIST.ITEM_NO AS "Shop Order",
ABAMS.T_WORKORDER_HIST.CUST_NAME AS Customer,
ABAMS.T_ITEM_POLICY.PL_LOC_DROP_ZONE_NO AS PLDZ,
ABAMS.T_WORKORDER_HIST.CONFIG_NO AS Configuration,
ASMBLYTST.V_EDP_ENG_LAST_ABSN.LAST_ASMBLY_ABSN AS "Last Sta",
ASMBLYTST.V_LAST_ENG_LOCATION.LAST_ASMBLY_LOC,
ASMBLYTST.V_LAST_ENG_LOCATION.LAST_MES_LOC,
ASMBLYTST.V_LAST_ENG_LOCATION.LAST_ASMBLY_TIME,
ASMBLYTST.V_LAST_ENG_LOCATION.LAST_MES_TIME
FROM ABAMS.T_WORKORDER_HIST
LEFT JOIN ABAMS.V_SERIAL_LINESET
ON ABAMS.V_SERIAL_LINESET.SERIAL_NO = ABAMS.T_WORKORDER_HIST.SERIAL_NO
LEFT JOIN ASMBLYTST.V_EDP_ENG_LAST_ABSN
ON ASMBLYTST.V_EDP_ENG_LAST_ABSN.SERIAL_NO = ABAMS.T_WORKORDER_HIST.SERIAL_NO
LEFT JOIN ASMBLYTST.V_SEQ_SERIAL_ALL
ON ASMBLYTST.V_SEQ_SERIAL_ALL.SERIAL_NO = ABAMS.T_WORKORDER_HIST.SERIAL_NO
LEFT JOIN ABAMS.T_ITEM_POLICY
ON ABAMS.T_ITEM_POLICY.ITEM_NO = ABAMS.T_WORKORDER_HIST.ITEM_NO
LEFT JOIN ABAMS.T_CUR_STATUS
ON ABAMS.T_CUR_STATUS.SERIAL_NO = ABAMS.T_WORKORDER_HIST.SERIAL_NO
INNER JOIN ASMBLYTST.V_LAST_ENG_LOCATION
ON ASMBLYTST.V_LAST_ENG_LOCATION.SERIAL_NO = ABAMS.T_WORKORDER_HIST.SERIAL_NO
WHERE ABAMS.T_WORKORDER_HIST.LINE_NO = 10
AND (ABAMS.T_WORKORDER_HIST.STATE = 'PROD'
OR ABAMS.T_WORKORDER_HIST.STATE = 'SCHED')
AND ASMBLYTST.V_SEQ_SERIAL_ALL.BUILD_DATE BETWEEN TRUNC(SysDate) - 10 AND TRUNC(SysDate) + 1
AND (ABAMS.V_SERIAL_LINESET.LINESET_DATE IS NOT NULL
OR ABAMS.V_SERIAL_LINESET.LINESET_DATE IS NULL)
AND (ASMBLYTST.V_EDP_ENG_LAST_ABSN.LAST_ASMBLY_ABSN < '1800'
OR ASMBLYTST.V_EDP_ENG_LAST_ABSN.LAST_ASMBLY_ABSN IS NULL)
ORDER BY ASMBLYTST.V_EDP_ENG_LAST_ABSN.LAST_ASMBLY_ABSN DESC Nulls Last,
ABAMS.V_SERIAL_LINESET.LINESET_DATE Nulls Last,
ASMBLYTST.V_SEQ_SERIAL_ALL.BUILD_DATE,
ASMBLYTST.V_SEQ_SERIAL_ALL.SEQ_NO,
ASMBLYTST.V_SEQ_SERIAL_ALL.SEQ_NO_EXT`
Here are some of the records I get from the table
ASMBLYTST.V_LAST_ENG_LOCATION.LAST_ASMBLY_TIME
2018-06-14 01:28:25
2018-06-14 01:29:26
2018-06-14 01:27:30
2018-06-13 22:44:03
2018-06-14 01:28:45
2018-06-14 01:27:37
2018-06-14 01:27:41
What I essentially want is for
2018-06-13 22:44:03
to be excluded from the query because it is not within the 5 minute window from the latest record Which in this data set is
2018-06-14 01:29:26
The one dynamic problem i seem to have is that the values for date-time are constantly updating.
Any ideas?
Thank you!

Here are two different solutions, each uses a table called "ASET".
ASET contains 20 records 1 minute apart:
WITH
aset (ttime, cnt)
AS
(SELECT systimestamp AS ttime, 1 AS cnt
FROM DUAL
UNION ALL
SELECT ttime + INTERVAL '1' MINUTE AS ttime, cnt + 1 AS cnt
FROM aset
WHERE cnt < 20)
select * from aset;
Now using ASET for our data, the following query finds the maximum date in ASET, and restricts the results to the six records within 5 minutes of ASET:
SELECT *
FROM aset
WHERE ttime >= (SELECT MAX (ttime)
FROM aset)
- INTERVAL '5' MINUTE;
An alternative is to use an analytic function:
with bset
AS
(SELECT ttime, cnt, MAX (ttime) OVER () - ttime AS delta
FROM aset)
SELECT *
FROM bset
WHERE delta <= INTERVAL '5' MINUTE

Related

Show specific rows in SQL by not existing in another table

I'm not very experienced in advanced SQL and stackoverflow, so I'm trying my best to explain what I need.
Let's say I have a table called 'Shift' and a table called 'Schedule'.
'Shift' has columns 'shift_id, shift_start, shift_end, shift_day,
shift_function_id'.
shift_start represents a start time.
shift_end represents a end time.
shift_day represents the number of the day in a week (0-6 starting on
sunday).
shift_function_id represents a function_id belonging to the shift.
'Schedule' has columns 'schedule_id, schedule_date, schedule_start,
schedule_end, schedule_function_id'.
schedule_date represents the date of a schedule.
schedule_start represents start time.
schedule_end represents end time.
schedule_function_id represents a function_id belonging to the
schedule.
What I'm trying to do is if a row from 'Shift' doesn't exist in table 'Schedule' with a given specific date where shift_start = schedule_start AND shift_end = schedule_end AND shift_function_id = schedule_function_id, then show the row.
Here's an example:
SELECT shift_id, shift_day, shift_start, shift_end, function_id, function_name, function_color
FROM Shift
LEFT JOIN Function
ON function_id = shift_function_id
WHERE shift_day = $day
AND NOT EXISTS (
SELECT 1
FROM Schedule
WHERE schedule_date = '$date' AND schedule_start = shift_start AND schedule_end = shift_end AND schedule_function_id = shift_function_id
)
ORDER BY function_name, shift_start, shift_end;
The problem is
If I have 2 shifts with the same starting and end time and table 'Schedule' contains ONE row with the same function_id and starting and end time, BOTH the 2 shifts won't show up.
Here's an example of the table content:
Schedule
schedule_id: 310
schedule_date: 2020-01-11
schedule_start: 16:30:00
schedule_end: 20:00:00
schedule_function_id: 27
Shift
shift_id: 45
shift_day: 6
shift_start: 16:30:00
shift_end: 20:00:00
shift_function_id: 27
shift_id: 46
shift_day: 6
shift_start: 16:30:00
shift_end: 20:00:00
shift_function_id: 27
BOTH 2 rows from 'Shift' dont show up anymore'.
What I want
I want if 'Schedule' only has 1 row which contains the same information as the given data in 'Shift', I want the other row to show up.
If 'Schedule' has 2 rows with the same information, none to show up. It just needs to depend on how many rows 'Schedule' has with the same information.
IMAGES
When nothing is filled in, it shows 2 rows with same start and end
When I put a record with same start and end time, it removes both shift rows
I need this, when I only fill in one record with same start and end time
The only way I can think of is to let NOT EXISTS clause remove all rows, Just append 1 row from duplicate rows with MIN (Or MAX) Shift_id -
SELECT shift_id,
shift_day,
shift_start,
shift_end,
function_id,
function_name,
function_color
FROM shift
LEFT JOIN function
ON function_id = shift_function_id
WHERE shift_day = $day
AND NOT EXISTS (SELECT 1
FROM schedule
WHERE schedule_date = '$date'
AND schedule_start = shift_start
AND schedule_end = shift_end
AND schedule_function_id = shift_function_id)
UNION ALL
SELECT Min(S1.shift_id),
S1.shift_day,
S1.shift_start,
S1.shift_end,
function_id,
function_name,
function_color
FROM shift S1
JOIN shift S2
ON S1.shift_id <> S2.shift_id
AND S1.shift_day = S2.shift_day
AND S1.shift_start = S2.shift_start
AND S1.shift_end = S2.shift_end
LEFT JOIN function
ON function_id = S1.shift_function_id
GROUP BY S1.shift_day,
S1.shift_start,
S1.shift_end,
function_id,
function_name,
function_color
ORDER BY function_name,
shift_start,
shift_end;
Here is working example.
If your version of MariaDB supports window functions, you could use row_number() in a subquery to disambiguate records that have the same (shift_start, shift_end, shift_function) in the shift_table, or the same (schedule_start, schedule_end, schedule_function) in the schedule table.
You can then use the record rank in the join conditions (I changed the NOT EXITS subquery to a LEFT JOIN antipattern, but this is essentially the same logic).
select s.*, f.*
from (
select
s.*,
row_number() over(
partition by shift_start, shift_end, shift_function
order by shift_id
) rn
from shift s
where shift_day = #shift_day
) s
left join (
select
c.*,
row_number() over(
partition by schedule_start, schedule_end, schedule_function
order by schedule_id
) rn
from schedule c
where schedule_date = #schedule_date
) c
on c.schedule_start = s.shift_start
and c.schedule_end = s.shift_end
and c.schedule_function_id = s.shift_function_id
and c.rn = s.rn
left join function f
on f.function_id = s.shift_function_id
where c.rn is null
order by f.function_name, s.shift_start, s.shift_end

Trying to calculate a SUM from another column in Materialized View

I am trying to calculate the sum of working days per month in a Oracle MV
Here is my request:
CREATE MATERIALIZED VIEW DIM_DATE_MV
BUILD IMMEDIATE
REFRESH COMPLETE ON DEMAND
START WITH sysdate NEXT (TRUNC(sysdate)+1) + 7 / 24
as SELECT
CAL.DATE_D as ID_DATE,
(CASE WHEN (
(TRIM(TO_CHAR(CAL.DATE_D,'Day','nls_date_language=english')) IN ('Saturday','Sunday')) OR
(TRIM(TO_CHAR(CAL.DATE_D,'DD-MM')) IN ('01-01', '01-05', '08-05', '14-07', '15-08', '01-11', '11-11', '25-12')) OR
(TO_CHAR(CAL.DATE_D, 'DD-MM-YYYY') IN (SELECT TO_CHAR(DOFF.DATE_OFF, 'DD-MM-YYYY') FROM ODSISIC.DAY_OFF DOFF where DOFF.IMPACT='ALL'))
) THEN 0 ELSE 1 END) as IS_WORKING_DAY,
(CASE WHEN TO_CHAR(CAL.DATE_D , 'YYYY-MM') = TO_CHAR(CAL.DATE_D , 'YYYY-MM') THEN (Select SUM(IS_WORKING_DAY) from DIM_DATE_MV group by CAL.YEAR_MONTH_NUM) ELSE 0 END)
as NB_WORKING_DAY_MONTH
FROM ODSISIC.ORACLE_CALENDAR CAL
LEFT JOIN ODSISIC.DAY_OFF DOFF
ON DOFF.DATE_OFF = CAL.DATE_D
IS_WORKING_DAY = 0 if it's Holidays, Weekend or Date in the table DATE_OFF which contains all holidays with a different date from year to year.
I want the SUM GROUP BY month of IS_WORKING_DAY = 1 in NB_WORKING_DAY_MONTH.
How can I calculate this SUM directly in my query rather than creating an intermediate table for my join with the DAY_OFF table ?
Thanks :)
After thinking intelligently, I resolved by redoing my SQL query :
CREATE MATERIALIZED VIEW DIM_DATE_MV
BUILD IMMEDIATE
REFRESH COMPLETE ON DEMAND
START WITH sysdate NEXT (TRUNC(sysdate)+1) + 7 / 24
as SELECT
CAL.DATE_D as ID_DATE,
IS_WORKING_DAY as IS_WORKING_DAY,
A.SUM as NB_WORKING_DAY_MONTH
FROM (SELECT SUM(IS_WORKING_DAY) as SUM, OCAL.YEAR_MONTH_NUM as ID_MONTH from ODSISIC.ORACLE_CALENDAR OCAL group by OCAL.YEAR_MONTH_NUM) A
INNER JOIN ODSISIC.ORACLE_CALENDAR CAL
on CAL.YEAR_MONTH_NUM = A.ID_MONTH
LEFT JOIN ODSISIC.DAY_OFF DOFF
ON DOFF.DATE_OFF = CAL.DATE_D
;
I calculated the workdays before creating the view (which implies that my table DATE_OFF must be fed before ORACLE_CALENDAR)
I added a join to populate my table according to the id_month.
Its working fine now

List all months with a total regardless of null

I have a very small SQL table that lists courses attended and the date of attendance. I can use the code below to count the attendees for each month
select to_char(DATE_ATTENDED,'YYYY/MM'),
COUNT (*)
FROM TRAINING_COURSE_ATTENDED
WHERE COURSE_ATTENDED = 'Fire Safety'
GROUP BY to_char(DATE_ATTENDED,'YYYY/MM')
ORDER BY to_char(DATE_ATTENDED,'YYYY/MM')
This returns a list as expected for each month that has attendees. However I would like to list it as
January 2
February 0
March 5
How do I show the count results along with the nulls? My table is very basic
1234 01-JAN-15 Fire Safety
108 01-JAN-15 Fire Safety
1443 02-DEC-15 Healthcare
1388 03-FEB-15 Emergency
1355 06-MAR-15 Fire Safety
1322 09-SEP-15 Fire Safety
1234 11-DEC-15 Fire Safety
I just need to display each month and the total attendees for Fire Safety only. Not used SQL developer for a while so any help appreciated.
You would need a calendar table to select a period you want to display. Simplified code would look like this:
select to_char(c.Date_dt,'YYYY/MM')
, COUNT (*)
FROM calendar as c
left join TRAINING_COURSE_ATTENDED as tca
on tca.DATE_ATTENDED = c.Date_dt
WHERE tca.COURSE_ATTENDED = 'Fire Safety'
and c.Date_dt between [period_start_dt] and [period_end_dt]
GROUP BY to_char(c.Date_dt,'YYYY/MM')
ORDER BY to_char(c.Date_dt,'YYYY/MM')
You can create your own set required year month's on-fly with 0 count and use query as below.
Select yrmth,sum(counter) from
(
select to_char(date_attended,'YYYYMM') yrmth,
COUNT (1) counter
From TRAINING_COURSE_ATTENDED Where COURSE_ATTENDED = 'Fire Safety'
Group By Y to_char(date_attended,'YYYYMM')
Union All
Select To_Char(2015||Lpad(Rownum,2,0)),0 from Dual Connect By Rownum <= 12
)
group by yrmth
order by 1
If you want to show multiple year's, just change the 2nd query to
Select To_Char(Year||Lpad(Month,2,0)) , 0
From
(select Rownum Month from Dual Connect By Rownum <= 12),
(select 2015+Rownum-1 Year from Dual Connect By Rownum <= 3)
Try this :
SELECT Trunc(date_attended, 'MM') Month,
Sum(CASE
WHEN course_attended = 'Fire Safety' THEN 1
ELSE 0
END) Fire_Safety
FROM training_course_attended
GROUP BY Trunc(date_attended, 'MM')
ORDER BY Trunc(date_attended, 'MM')
Another way to generate a calendar table inline:
with calendar (month_start, month_end) as
( select add_months(date '2014-12-01', rownum)
, add_months(date '2014-12-01', rownum +1) - interval '1' second
from dual
connect by rownum <= 12 )
select to_char(c.month_start,'YYYY/MM') as course_month
, count(tca.course_attended) as attended
from calendar c
left join training_course_attended tca
on tca.date_attended between c.month_start and c.month_end
and tca.course_attended = 'Fire Safety'
group by to_char(c.month_start,'YYYY/MM')
order by 1;
(You could also have only the month start in the calendar table, and join on trunc(tca.date_attended,'MONTH') = c.month_start, though if you had indexes or partitioning on tca.date_attended that might be less efficient.)

Joining Tables on Time, IF NULL edit time by 1 minute

I have two tables.
Table 1 = My Trades
Table 2 = Market Trades
I want query the market trade 1 minute prior to my trade. If there is no market trade in Table 2 that is 1 minute apart from mine then I want to look back 2 minutes and so on till I have a match.
Right now my query gets me 1 minute apart but I cant figure out how to get 2 minutes apart if NULL or 3 minutes apart if NULL (up to 30 minutes). I think it would best using a variable but im not sure the best way to approach this.
Select
A.Ticker
,a.date_time
,CONVERT(CHAR(16),a.date_time - '00:01',120) AS '1MINCHANGE'
,A.Price
,B.Date_time
,B.Price
FROM
Trade..MyTrade as A
LEFT JOIN Trade..Market as B
on (a.ticker = b.ticker)
and (CONVERT(CHAR(16),a.date_time - '00:01',120) = b.Date_time)
There is no great way to do this in MySQL. But, because your code looks like SQL Server, I'll show that solution here, using APPLY:
select t.Ticker ,
convert(CHAR(16), t.date_time - '00:01', 120) AS '1MINCHANGE',
t.Price,
m.Date_time,
m.Price
from Trade..MyTrade as t outer apply
(select top 1 m.*
from Trade..Market m
where a.ticker = b.ticker and
convert(CHAR(16), t.date_time - '00:01', 120) >= b.Date_time)
order by m.DateTime desc
) m;

Concatenation of max_times within 15 minute time intervals SQL

I am trying to write a sql query that pulls an ID and concatenates the max times within 15 minute time intervals starting from the first time.
i.e. for one log_id data might be
101 01:01
101 01:08
101 01:23
101 02:01
101 02:10
101 02:16
we would want to display
101 01:01, 01:08, 01:23, 2:01, 2:16
any ideas?
here is the starting query we are using:
select
ol.log_id,
ifm.meas_value,
ifm.measure_id,
ifm.recorded_time
from meas ifm
inner join rec ifr on ifm.fsd_id = ifr.fsd_id
inner join pe on ifr.data_id = pe.data_id
inner join record_summary f on pe.n_id = f.n_id
inner join pe2 on pe.t_id = pe2.t_id and pe.date = pe2.date and pe2.type = 51
inner join log l on pe2.ata_id = l.data_id and l.date = pe2.date
where ifm.measure_id in ('891')
and ol.date >= trunc(sysdate - 3)
for each log_id there will be multiple recorded times we want to pull the first time, and the maximum time in every 15 minute interval until the last time. These times will be concatenated to a list.
We tried a listagg with all the values, but there are too many times so the end-user wants to only see one value for every 15 minutes.
Basically you have 3 questions in one:
How to round date field to 15 min?
https://community.oracle.com/thread/1042863
ceil(to_number(to_char(test_timestamp, 'yyyymmddhh24mi'))/15) * 15
How to get the row from a group?
That is a very popular question, you will find something here:
https://stackoverflow.com/questions/tagged/oracle+greatest-n-per-group?sort=votes&pageSize=30
How to aggregate over string values to concatenate within a group:
LISTAGG(convertedTimeField, ' ')
Try to combine all three pieces in one query to get required result.
To simplify working with the following table:
CREATE TABLE Test ( id NUMBER, log DATE );
(Note: DATE in oracle is actually dateTime. String concatenation is done with ||.)
Your given list seems to indicate, that when the max and the min is the same you only display it once, this can be done by grouping twice:
SELECT
O.id,
O.logDate,
LISTAGG(O.logTimes, ', ') WITHIN GROUP (ORDER BY O.logTimes) logTimes
FROM (
SELECT
O.id,
O.logDate,
O.G G,
CASE
WHEN TO_CHAR(MIN(O.log), 'HH24:MI') <> TO_CHAR(MAX(O.log), 'HH24:MI') THEN
TO_CHAR(MIN(O.log), 'HH24:MI') || ', ' || TO_CHAR(MAX(O.log), 'HH24:MI')
ELSE
TO_CHAR(MIN(O.log), 'HH24:MI')
END logTimes
FROM (
SELECT
T.id,
T.log,
TRUNC(T.log) logDate,
TO_CHAR(T.log, 'HH24') || FLOOR(TO_NUMBER(TO_CHAR(T.log, 'MI')) / 15) G
FROM
Test T
) O
GROUP BY
O.id, O.logDate, O.G
) O
GROUP BY
O.id, O.logDate
;
See SQL fiddle.