Coldfusion - How to parse and segment out data from an email file - sql-server

I am trying to parse email files that will be coming periodically for data that is contained within. We plan to setup cfmail to get the email within the box within CF Admin to run every minute.
The data within the email consists of name, code name, address, description, etc. and will have consistent labels so we are thinking of performing a loop or find function for each field of data. Would that be a good start?
Here is an example of email data:
INCIDENT # 12345
LONG TERM SYS# C12345
REPORTED: 08:39:34 05/20/19 Nature: FD NEED Address: 12345 N TEST LN
City: Testville
Responding Units: T12
Cross Streets: Intersection of: N Test LN & W TEST LN
Lat= 39.587453 Lon= -86.485021
Comments: This is a test post. Please disregard
Here's a picture of what the data actually looks like:
So we would like to extract the following:
INCIDENT
LONG TERM SYS#
REPORTED
Nature
Address
City
Responding Units
Cross Streets
Comments
Any feedback or suggestions would be greatly appreciated!

Someone posted this but it was apparently deleted. Whoever it was I want to thank you VERY MUCH as it worked perfectly!!!!
Here is the function:
<!---CREATE FUNCTION [tvf-Str-Extract] (#String varchar(max),#Delimiter1
varchar(100),#Delimiter2 varchar(100))
Returns Table
As
Return (
with cte1(N) as (Select 1 From (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))
N(N)),
cte2(N) as (Select Top (IsNull(DataLength(#String),0)) Row_Number() over (Order By
(Select NULL)) From (Select N=1 From cte1 N1,cte1 N2,cte1 N3,cte1 N4,cte1 N5,cte1 N6) A
),
cte3(N) as (Select 1 Union All Select t.N+DataLength(#Delimiter1) From cte2 t
Where Substring(#String,t.N,DataLength(#Delimiter1)) = #Delimiter1),
cte4(N,L) as (Select S.N,IsNull(NullIf(CharIndex(#Delimiter1,#String,s.N),0)-
S.N,8000) From cte3 S)
Select RetSeq = Row_Number() over (Order By N)
,RetPos = N
,RetVal = left(RetVal,charindex(#Delimiter2,RetVal)-1)
From ( Select *,RetVal = Substring(#String, N, L) From cte4 ) A
Where charindex(#Delimiter2,RetVal)>1
)
And here is the CF code that worked:
<cfquery name="body" datasource="#Application.dsn#">
Declare #S varchar(max) ='
INCIDENT 12345
LONG TERM SYS C12345
REPORTED: 08:39:34 05/20/19 Nature: FD NEED Address: 12345 N TEST
LN City: Testville
Responding Units: T12
Cross Streets: Intersection of: N Test LN & W TEST LN
Lat= 39.587453 Lon= -86.485021
Comments: This is a test post. Please disregard
'
Select Incident = ltrim(rtrim(B.RetVal))
,LongTerm = ltrim(rtrim(C.RetVal))
,Reported = ltrim(rtrim(D.RetVal))
,Nature = ltrim(rtrim(E.RetVal))
,Address = ltrim(rtrim(F.RetVal))
,City = ltrim(rtrim(G.RetVal))
,RespUnit = ltrim(rtrim(H.RetVal))
,CrossStr = ltrim(rtrim(I.RetVal))
,Comments = ltrim(rtrim(J.RetVal))
From (values (replace(replace(#S,char(10),''),char(13),' ')) )A(S)
Outer Apply [dbo].[tvf-Str-Extract](S,'INCIDENT' ,'LONG
TERM' ) B
Outer Apply [dbo].[tvf-Str-Extract](S,'LONG TERM SYS'
,'REPORTED' ) C
Outer Apply [dbo].[tvf-Str-Extract](S,'REPORTED:' ,'Nature'
) D
Outer Apply [dbo].[tvf-Str-Extract](S,'Nature:'
,'Address' ) E
Outer Apply [dbo].[tvf-Str-Extract](S,'Address:' ,'City'
) F
Outer Apply [dbo].[tvf-Str-Extract](S,'City:'
,'Responding ') G
Outer Apply [dbo].[tvf-Str-Extract](S,'Responding Units:','Cross'
) H
Outer Apply [dbo].[tvf-Str-Extract](S,'Cross Streets:' ,'Lat'
) I
Outer Apply [dbo].[tvf-Str-Extract](S+'|||','Comments:' ,'|||'
) J
</cfquery>
<cfoutput>
B. #body.Incident#<br>
C. #body.LongTerm#<br>
D. #body.Reported#<br>

SQL tends to have limited string functions, so it isn't the best tool for parsing. If the email content is always in that exact format, you could use either plain string functions or regular expressions to parse it. However, the latter is more flexible.
I suspect the content actually does contain new lines, which would make for simpler parsing. However, if you prefer searching for content in between two labels, regular expressions would do the trick.
Build an array of the label names (only). Loop through the array, grabbing a pair of labels: "current" and "next". Use the two values in a regular expression to extract the text in between them:
label &"\s*[##:=](.*?)"& nextLabel
/* Explanation: */
label - First label name (example: "Incident")
\s* - Zero or more spaces
[##:=] - Any of these characters: pound sign, colon or equal sign
(.*?) - Group of zero or more characters (non-greedy)
nextLabel - Next label (example: "Long Term Sys")
Use reFindNoCase() to get details about the position and length of matched text. Then use those values in conjunction with mid() to extract the text.
Note, newer versions like ColdFusion 2016+ automagically extract the text under a key named MATCH
The newer CF2016+ syntax is slicker, but something along these lines works under CF10:
emailBody = "INCIDENT # 12345 ... etc.... ";
labelArray = ["Incident", "Long Term Sys", "Reported", ..., "Comments" ];
for (pos = 1; pos <= arrayLen(labelArray); pos++) {
// get current and next label
hasNext = pos < arrayLen(labelArray);
currLabel = labelArray[ pos ];
nextLabel = (hasNext ? labelArray[ pos+1 ] : "$");
// extract label and value
matches = reFindNoCase( currLabel &"\s*[##:=](.*?)"& nextLabel, emailBody, 1, true);
if (arrayLen(matches.len) >= 2) {
results[ currLabel ] = mid( emailBody, matches.pos[2], matches.len[2]);
}
}
writeDump( results );
Results:

Related

SQL Server : Sum function is returning wrong value

So this is my query
select a.ERSDataValues_ERSCommodity_ID,c.ersGeographyDimension_country,
b.ERSTimeDimension_Year,
sum(a.ERSDataValues_AttributeValue) as Total
from cosd.ERSDataValues a ,cosd.ERSTimeDimension_LU
b,cosd.ERSGeographyDimension_LU c
where a.ERSDataValues_ERSCommodity_ID in (SELECT
ERSBusinessLogic_InputDataSeries
FROM [AnimalProductsCoSD].[CoSD].[ERSBusinessLogic]
where ERSBusinessLogic_InputGeographyDimensionID = 7493
and ERSBusinessLogic_InputTimeDimensionValue = 'all months'
and ERSBusinessLogic_Type = 'time aggregate')
and a.ERSDataValues_ERSTimeDimension_ID = b.ERSTimeDimension_ID
and c.ersGeographyDimension_country != 'WORLD'
and a.ERSDataValues_ERSGeography_ID=c.ERSGeographyDimension_ID
group by b.ERSTimeDimension_Year,a.ERSDataValues_ERSCommodity_ID,
c.ersGeographyDimension_country
order by b.ERSTimeDimension_Year,a.ERSDataValues_ERSCommodity_ID
This is the updated query, I know it is long but I am facing the same issue the sum does not match with values when I sum up manually
It's most likely a problem with your JOINs (which you aren't technically using). Try removing the SUM and GROUP functions and see if you're getting duplicate rows (maybe limit your time frame to get fewer rows if this is a large data set):
SELECT b.TimeDimension_Year ,
c.GeographyDimension_country ,
a.DataValues_AttributeValue
FROM cosd.DataValues a ,
cosd.TimeDimension_LU b ,
cosd.GeographyDimension_LU c
WHERE a.DataValues_Commodity_ID = 2257
AND a.DataValues_TimeDimension_ID = b.TimeDimension_ID
AND c.GeographyDimension_ID = 7493
AND b.TimeDimension_Year = 2015;

Listagg for large data and included values in quotes

I would like to get all the type names of a user seperated in commas and included in single quotes. The problem I have is that &apos ; character is displayed as output instead of '.
Trial 1
SELECT LISTAGG(TYPE_NAME, ''',''') WITHIN GROUP (ORDER BY TYPE_NAME)
FROM ALL_TYPES
WHERE OWNER = 'USER1';
ORA-01489: result of string concatenation is too long
01489. 00000 - "result of string concatenation is too long"
*Cause: String concatenation result IS more THAN THE maximum SIZE.
*Action: Make sure that the result is less than the maximum size.
Trial 2
SELECT '''' || RTRIM(XMLAGG(XMLELEMENT(E,TYPE_NAME,q'$','$' ).EXTRACT('//text()')
ORDER BY TYPE_NAME).GetClobVal(),q'$','$') AS LIST
FROM ALL_TYPES
WHERE OWNER = 'USER1';
&apos ;TYPE1&apos ;,&apos ;TYPE2&apos ;, ............... ,&apos;TYPE3&apos ;,&apos ;
Trial 3
SELECT
dbms_xmlgen.CONVERT(XMLAGG(XMLELEMENT(E,TYPE_NAME,''',''').EXTRACT('//text()')
ORDER BY TYPE_NAME).GetClobVal())
AS LIST
FROM ALL_TYPES
WHERE OWNER = 'USER1';
TYPE1&amp ;apos ;,&amp ;apos ;TYPE2&amp ;apos ;, ......... ,&amp ;apos ;TYPE3&amp ;apos ;,&amp ;apos ;
I don;t want to call replace function and then make substring as follow
With tbla as (
SELECT REPLACE('''' || RTRIM(XMLAGG(XMLELEMENT(E,TYPE_NAME,q'$','$' ).EXTRACT('//text()')
ORDER BY TYPE_NAME).GetClobVal(),q'$','$'),'&apos;',''') AS LIST
FROM ALL_TYPES
WHERE OWNER = 'USER1')
select SUBSTR(list, 1, LENGTH(list) - 2)
from tbla;
Is there any other way ?
use dbms_xmlgen.convert(col, 1) to prevent escaping.
According to Official docs, the second param flag is:
flag
The flag setting; ENTITY_ENCODE (default) for encode, and
ENTITY_DECODE for decode.
ENTITY_DECODE - 1
ENTITY_ENCODE - 0 default
Try this:
select
''''||substr(s, 1, length(s) - 2) list
from (
select
dbms_xmlgen.convert(xmlagg(xmlelement(e,type_name,''',''')
order by type_name).extract('//text()').getclobval(), 1) s
from all_types
where owner = 'USER1'
);
Tested the similar code below with 100000 rows:
with t (s) as (
select level
from dual
connect by level < 100000
)
select
''''||substr(s, 1, length(s) - 2)
from (
select
dbms_xmlgen.convert(xmlagg(XMLELEMENT(E,s,''',''') order by s desc).extract('//text()').getClobVal(), 1) s
from t);

An aggregate may not appear in the WHERE clause unless it is in a subquery

I am trying to use below query to sort out the consumer list based on
1)actual count and then
2)subcount based on values as in Sla_state =1 and result =0 ..
Query ..
select consumer as "Consumer", class_name as "Service", count(consumer) as "totalcount", avg(responsetime) as "AvgResponseTime (ms)", max(responsetime) as "Max ResponseTime (ms)" , sla_state as "sla", result as "result_state" , count(1) as "subcount"
from
DPOWER.business_transaction bt join DPOWER.mmfg_business_transaction mbt on
(bt.business_trans_id = mbt.business_trans_id) join DPOWER.transaction_class tc on (bt.class_id = tc.class_id) and sla_state = 1 and result=0
where
(bt.starttime >= '20150701000000000000' and bt.endtime <= '20150801000000000000') group by consumer, sla_state, result, class_name order by consumer
The above query worked ..but I am able to get only the subcount and not the total count of the consumers. Below is the three table structures. Can anyone figure out how to get the total count .( i tried all possible way like count (*) etc but that didnt work out..also if I use aliases I get "multipart idenfier not bound" error.
Could you mean this in your WHERE clause ??
where
consumer in
(
select
consumer
from
DPOWER.business_transaction
where
sla_state = 1
and result=0
)
and (bt.starttime >= '20150701000000000000'
and bt.endtime <= '20150801000000000000')
To get you started, here's an idea:
select
bt1.consumer as "Consumer",
count(bt1.*) as totalcount,
count(bt2.consumer) as subcount
from
DPOWER.business_transaction bt1
left outer join DPOWER.business_transaction bt2
on bt1.consumer = bt2.consumer
and bt2.sla_state = 1
and result=0
group by
bt1.consumer
order by consumer

Exclude value from query where already returned in rows above

I am writing a query which will be used to populate a report used for putting away stock in a warehouse.
The report has 3 parameters, a source stock location, source bin number and a destination stock location.
The stock will be currently held in source stock location and bin number.
The destination stock location is where the stock needs to be moved to.
Each Variant has a default bin number in each stock location.
Multiple variants can have the same default bin number.
Bin numbers may not be alphabetical around the warehouse, and so each bin number is assigned a walk route, as the most efficient walking route around the warehouse.
The report will look at the default bin number associated with the item in the destination stock location, and if empty, offer that as the put away suggestion.
If the default bin number is not empty, it will look for the next available empty bin in the destination stock location (where walk route is higher than default bin), and then offer that as the put away suggestion.
The query works fine, and does exactly that, however it is reporting the same bin as previous "NextBinNo" suggestions in rows above.
How can I get the OUTER APPLY NextBinNo to filter out any previously suggested bins in higher rows of data? Also if two items have the same default bin number, it should use the NextBinNo for the second row with this default bin no.
My current query:
Select
row_number() Over(Order by DestSL.sl_id) as RowNo,
Stock_location.sl_name,
bin_number.bn_bin_number,
variant_detail.vad_variant_code,
variant_detail.vad_description,
variant_transaction_header.vth_current_quantity,
variant_transaction_header.vth_batch_number,
purchase_order_header.poh_order_number,
supplier_detail.sd_ow_account,
DestSL.sl_id as 'DestinationSLID',
DestSL.sl_name as 'DestinationStockLocation',
DestDefaultBin.bn_bin_number as 'DestinationDefaultBin',
DestDefaultBin.bn_walk_route as 'DestinationDefaultWalkRoute',
isnull(DestDefaultBinQty.BinQty,0) as QtyInDefaultBin,
NextBinNo.NextBinNo as 'NextBinNo',
NextBinNo.NextWalkRoute as 'NextBinWalkRoute',
isnull(NextBinNo.BinQty,0) as 'NextBinQty',
case when DestDefaultBin.bn_bin_number is null
then 'Not Stocked in This Location'
Else
case when isnull(DestDefaultBinQty.BinQty,0) > 0
then
case when NextBinNo.NextBinNo is NULL
then 'No Free Bin'
Else NextBinNo.NextBinNo
End
Else DestDefaultBin.bn_bin_number
End
End as 'Put Away Destination'
From variant_transaction_header
join bin_number on bin_number.bn_id = variant_transaction_header.vth_bn_id
join stock_location on stock_location.sl_id = variant_transaction_header.vth_sl_id
join variant_detail on variant_detail.vad_id = variant_transaction_header.vth_vad_id
join transaction_type on transaction_Type.tt_id = variant_transaction_header.vth_tt_id
left join purchase_order_line on purchase_order_line.pol_id = variant_transaction_header.vth_pol_id
left join purchase_order_header on purchase_order_header.poh_id = purchase_order_line.pol_poh_id
left join supplier_detail on supplier_detail.sd_id = purchase_order_header.poh_sd_id
join stock_location DestSL on DestSL.sl_id = #DestinationStockLoc
left join variant_stock_location DestVSL on DestVSL.vsl_vad_id = variant_detail.vad_id and DestVSL.vsl_sl_id = DestSL.sl_id
left join bin_number DestDefaultBin on DestDefaultBin.bn_id = DestVSL.vsl_bn_id
left join
(select sum(variant_transaction_header.vth_current_quantity) as BinQty,
variant_transaction_header.vth_bn_id
from variant_transaction_header
join transaction_type on transaction_Type.tt_id = variant_transaction_header.vth_tt_id
Where variant_transaction_header.vth_current_quantity > 0
and transaction_type.tt_transaction_type = 'IN' and transaction_Type.tt_update_current_qty = 1
Group by variant_transaction_header.vth_bn_id) as DestDefaultBinQty on DestDefaultBinQty.vth_bn_id = DestDefaultBin.bn_id
Outer Apply
(select top 1
row_number() Over(Order by NextBin.bn_bin_number) as RowNo,
NextBin.bn_bin_number as NextBinNo,
NextBin.bn_walk_route as NextWalkRoute,
BinQty.BinQty
from
Stock_location DestSL
Join bin_number NextBin on NextBin.bn_sl_id = DestSL.sl_id
left join
(select sum(variant_transaction_header.vth_current_quantity) as BinQty,
variant_transaction_header.vth_bn_id
from variant_transaction_header
join transaction_type on transaction_Type.tt_id = variant_transaction_header.vth_tt_id
Where variant_transaction_header.vth_current_quantity > 0
and transaction_type.tt_transaction_type = 'IN' and transaction_Type.tt_update_current_qty = 1
Group by variant_transaction_header.vth_bn_id) as BinQty on BinQty.vth_bn_id = NextBin.bn_id
Where NextBin.bn_sl_id = #DestinationStockLoc
and NextBin.bn_walk_route > DestDefaultBin.bn_walk_route
And isnull(BinQty.BinQty,0) = 0
order by NextBin.bn_walk_route, nextbin.bn_bin_number) as NextBinNo
where variant_transaction_header.vth_current_quantity > 0
and transaction_type.tt_transaction_type = 'IN' and transaction_Type.tt_update_current_qty = 1
and stock_location.sl_id = #SourceStockLoc and bin_number.bn_id = #SourceBinNo
You can see my current results below:
Row2 is using the NextBinNo as the default bin has stock.
Row3 is also suggesting using AA08A2 as the next bin.
Row 6 is currently suggesting AA01A2 but that has already been suggested in Row1.
Just to answer this, in the end I could not achieve what I wanted directly in SQL.
The data is ultimately being returned to a report writer which can run Visual Basic code.
I had to execute the NextBinNo sub query separately in VB.
In VB I can define a string which contains a list of all the "used" bin numbers and so on each row this is referenced in the WHERE of the query to check it has not been used in a row above.
I could then return this value as a new column dynamically inserted in the dataset at run time.

SQL Filtering on unique events and datetime

I did ask this question before but deleted the post.
Here is my question: I have a table in SQL Server with multiple events in the table, I am trying to filter on the events and the datetime between the events on a certain X min.
I need help with T-SQL queries.
What can use use to do the following (any links will be appreciated):
take event_id and datetime of device, compare to a list of other event_id's to see if there are any other events withing the last 24 min for that device.
What would work for me? Nested queries? Where clause? CTE?
I have tried 6 queries but not getting the result I need, do I use (max) datetime and then compare on event_ID's?
I know this is not much but any help or links would be appreciated....
On Request (Full Query no changes)
1.
SELECT A.[Unit_id]
,A.[TransDate]
,A.[event_id]
,A.[event_msg]
,B.[TransDate]
,B.[event_id]
,B.[event_msg]
FROM
(Select
A.[Unit_id]
,MAX(A.[TransDate]) AS Transdate
,A.[event_id]
,A.[event_msg]
FROM [JammingEvents].[dbo].[EventLogExtended] AS A
WHERE event_id = '345'
GROUP BY A.[Unit_id]
,A.[event_id]
,A.[event_msg]
) AS A
INNER JOIN
(SELECT
B.[Unit_id]
,MAX(B.[TransDate]) AS Transdate
,B.[event_id]
,B.[event_msg]
FROM [JammingEvents].[dbo].[EventLogExtended] AS B
WHERE B.event_id = '985'
GROUP BY B.[Unit_id]
,B.[event_id]
,B.[event_msg]
) AS B
ON
A.Unit_id = B.Unit_id
WHERE (B.TransDate BETWEEN DATEADD(MINUTE,-24,A.Transdate) AND DATEADD(MINUTE,24,A.Transdate))
this works but only if you compare 2 event Id's. the problem is i need to do this on a bulk scale.
2.
SET NOCOUNT ON
GO
SELECT MAX(ELE.Transdate) AS Transdate
,ELE.[Unit_id]
,ELE.[event_id]
,ELE.[event_msg]
,ELE.[Latitude]
,ELE.[Longitude]
,ELE.[date_ack]
,ELE.[user_id_ack]
,ELE.[date_closed]
,ELE.[user_id_closed]
,ELE.[action]
,ELE.[EventSeqNo]
,ELE.[MsgSeqNo]
,ELE.[Speed]
,ELE.[Heading]
,ELE.[Status1]
,ELE.[Status2]
,ELE.[GeoLocation]
,ELE.[Reg_No]
,ELE.[Company]
,ELE.[Fleet_Code]
,ELE.[IgnStatus]
,ELE.[comboActioned]
FROM
[JammingEvents].[dbo].[EventLogExtended] ELE
INNER JOIN
(
SELECT
F.Transdate ,F.[Unit_id] ,F.[event_id] ,F.[event_msg] ,F.[Latitude] ,F.[Longitude] ,F.[date_ack] ,F.[user_id_ack]
,F.[date_closed] ,F.[user_id_closed] ,F.[action] ,F.[EventSeqNo] ,F.[MsgSeqNo] ,F.[Speed] ,F.[Heading] ,F.[Status1]
,F.[Status2] ,F.[GeoLocation] ,F.[Reg_No] ,F.[Company] ,F.[Fleet_Code] ,F.[IgnStatus] ,F.[comboActioned]
FROM [JammingEvents].[dbo].[EventLogExtended] AS F
WHERE
F.event_id IN
('302'
,'303'
,'304'
,'305'
,'309'
,'340'
,'341'
,'345'
,'962'
,'963'
,'973'
,'974'
,'975'
,'976'
,'985'
,'987'
,'989'
,'C220'
,'C222'
,'C224'
,'C227'
,'C228')
) AS A
ON
ELE.Unit_id = A.Unit_id
INNER JOIN
(
SELECT
G.Transdate ,G.[Unit_id] ,G.[event_id] ,G.[event_msg] ,G.[Latitude] ,G.[Longitude] ,G.[date_ack]
,G.[user_id_ack] ,G.[date_closed] ,G.[user_id_closed] ,G.[action] ,G.[EventSeqNo] ,G.[MsgSeqNo] ,G.[Speed] ,G.[Heading]
,G.[Status1] ,G.[Status2] ,G.[GeoLocation] ,G.[Reg_No] ,G.[Company] ,G.[Fleet_Code] ,G.[IgnStatus] ,G.[comboActioned]
FROM [JammingEvents].[dbo].[EventLogExtended] AS G
WHERE
G.event_id = '345'
) AS B
ON
A.Unit_id = B.Unit_id
WHERE (ELE.TransDate BETWEEN DATEADD(MINUTE,-24,A.Transdate) AND DATEADD(MINUTE,24,A.Transdate))
AND
(ELE.event_id IN
(
'302'
,'303'
,'304'
,'305'
,'309'
,'340'
,'341'
,'345'
,'962'
,'963'
,'973'
,'974'
,'975'
,'976'
,'985'
,'987'
,'989'
,'C220'
,'C222'
,'C224'
,'C227'
,'C228'
))
AND
(ELE.action = '0')
GROUP BY ELE.TransDate
,A.Unit_id
,ELE.[Unit_id]
,ELE.[event_id]
,ELE.[event_msg]
,ELE.[Latitude]
,ELE.[Longitude]
,ELE.[date_ack]
,ELE.[user_id_ack]
,ELE.[date_closed]
,ELE.[user_id_closed]
,ELE.[action]
,ELE.[EventSeqNo]
,ELE.[MsgSeqNo]
,ELE.[Speed]
,ELE.[Heading]
,ELE.[Status1]
,ELE.[Status2]
,ELE.[GeoLocation]
,ELE.[Reg_No]
,ELE.[Company]
,ELE.[Fleet_Code]
,ELE.[IgnStatus]
,ELE.[comboActioned]
ORDER BY ELE.TransDate, ELE.Unit_id DESC
so far this got me the closest but i have no idea what to do, i am very poor with SQL syntax and writing queries.
You could use an exists subquery to demand that there was another event for the same device within the preceeding 24 minutes:
select *
from Events e1
where exists
(
select *
from Events e2
where e2.Device_ID = e1.Device_ID
and e2.event_dt between
dateadd(minute, e1.event_dt, -24)
and e1.event_dt
)
Self join should work as well:
SELECT *, DATEDIFF(minute,b.transdate,a.transdate) diff FROM EventLogWxtended a
JOIN eventLogExtended b
ON (a.unit_id=b.unit_id AND b.transdate<a.transdate
AND DATEDIFF(minute,b.transdate,a.transdate)<24)

Resources