Problems with lag function(db2) - db2

I have a table that contains the following columns:
userid Designation load_dt audit_time type
A Engg 3/11/2015 22/06/2015 R
A Engg 4/11/2015 03/07/2015 D
A Engg 31/12/9999 4/10/2015 R
A doc 31/12/9999 22/06/2015 R
I am trying to create an output by tracking the change in audit_time on the basis of userid and designation. for first row, start date will be 01-01-1900 and if no other record exists then end date would be 12-31-9999. For userid = A, since the change happened on 03/07/2015, so first record gets start date from 01/01/1900 and got end dated on 03/07/2015. second record gets the start date as 03/07/2015 and remains active till 4/10/2015. The third change started from 04/10/2015 and has future end date.
userid Designation load_dt audit_time type start_date end_date
A Engg 3/11/2015 22/06/2015 R 01/01/1900 03/07/2015
A Engg 4/11/2015 03/07/2015 D **03/07/2015 4/10/2015
A Engg 31/12/9999 4/10/2015 R **4/10/2015 31/12/9999
A doc 31/12/9999 22/06/2015 R 01/01/1900 31/12/9999
I used the following query:
select userid, designation, LOAD_DT, AUDIT_TIME, TYPE
, coalesce(lag(AUDIT_TIME ) over (partition by userid, designation order by AUDIT_TIME),'1900-01-01') start
, coalesce(lead(AUDIT_TIME ) over (partition by userid, designation order by AUDIT_TIME),'9999-12-31') enddt
from DUMMY1;
I am getting the following results:
userid Designation load_dt audit_time type start_date end_date
A Engg 3/11/2015 22/06/2015 R 01/01/1900 03/07/2015
A Engg 4/11/2015 03/07/2015 D **22/06/2015 4/10/2015
A Engg 31/12/9999 4/10/2015 R **03/07/2015 31/12/9999
A doc 31/12/9999 22/06/2015 R 01/01/1900 31/12/9999
I hope I am able to explain my problem correctly.

I was able to solve the problem by just tweaking the lag calculation:
select userid, designation, LOAD_DT, AUDIT_TIME, TYPE
, case when lag(AUDIT_TIME ) over (partition by userid, designation order by AUDIT_TIME) then '1900-01-01' else audit_time end start
, coalesce(lead(AUDIT_TIME) over (partition by userid, designation order by AUDIT_TIME),'9999-12-31') enddt
from DUMMY1;

Related

In PostgreSQL, how can I optimize a query with which I obtain the differences between the current column and the immediately previous one?

I have this audit table
User
date
text
text 2
u1
2023-01-01
hi
yes
u1
2022-12-20
hi
no
u1
2022-12-01
hello
maybe
And I need as a result, something like this:
User
date
text
text 2
u1
2023-01-01
null
x
u1
2022-12-20
x
x
u1
2022-12-01
null
null
So I can know which column changed from the last time.
Something like this is working, but I think may be a way to optimize it? or at least generate a "more easy to look" query? (i need the information for almost 20 columns, not only 3)
SELECT
ta.audit_date,
ta.audit_user,
CASE
WHEN ta.audit_operation = 'I' THEN 'Insert'
WHEN ta.audit_operation = 'U' THEN 'Update'
END AS action,
CASE WHEN ta.column1 <> (SELECT column1
FROM audit_table ta1
WHERE ta1.id = 9207 AND ta1.audit_date < ta.audit_date
ORDER BY ta1.audit_date DESC
LIMIT 1)
THEN 'X' ELSE null END column1,
CASE WHEN ta.column2 <> (SELECT column2
FROM audit_table ta1
WHERE ta1.id = 9207 AND ta1.audit_date < ta.audit_date
ORDER BY ta1.audit_date DESC
LIMIT 1)
THEN 'X' ELSE null END column2,
CASE WHEN ta.column3 <> (SELECT column3
FROM audit_table ta1
WHERE ta1.id = 9207 AND ta1.audit_date < ta.audit_date
ORDER BY ta1.audit_date DESC
LIMIT 1)
THEN 'X' ELSE null END column3
FROM
audit_table ta
WHERE
ta.id = 9207
ORDER BY
audit_date DESC
Thank you!
I think you can just use the LAG() analytic function here. If I understand correctly:
SELECT *, CASE WHEN text != LAG(text) OVER (ORDER BY date) THEN 'x' END AS text_label,
CASE WHEN text2 != LAG(text) OVER (ORDER BY date) THEN 'x' END AS text2_label
FROM yourTable
ORDER BY date;

Passing Bind Variable in case statements in Oracle query

I want to pass Bind Variable (a Date) in a Case statement in order to achieve the following:
When User inputs a date and if that Date is falling on Monday, then the Case statement should fetch the value of Friday (meaning it should bypass the weekends and look for the values of a previous working day)
I tried to use the following query which works well when I use 'sysdate'
Select * from table_name
Where tradedate = trunc (sysdate - case to_char(sysdate, 'Dy')
when 'Mon' then 3 else 1 end);
but when I replace 'sysdate' with a Bind Variables, it gives me an error like:
tradedate = trunc (:sysdate1 - case to_char(:sysdate2, 'Dy')
when 'Mon' then 3 else 1 end);
ORA-00932: inconsistent datatypes: expected DATE got NUMBER
00932. 00000 - "inconsistent datatypes: expected %s got %s"
Can't we use Bind Variables in Case statement in oracle queries? If so, Can someone please give me any alternate solution to my problem stated above?
Any help would be really appreciated.
Below is the complete code:
select * from (
SELECT
S."TRADEDATE",S."ACCOUNT_NAME",S."BOOKING_AMOUNT",S."ACCOUNT_NUMBER",(CASE WHEN BOOKING_AMOUNT <0 THEN S."CREDIT" ELSE S."DEBIT" END) AS "DEBIT" , (CASE WHEN BOOKING_AMOUNT <0 THEN S."DEBIT" ELSE S."CREDIT" END) AS "CREDIT",
U.VALUE_DT , U.AC_NO , NVL(U.BOOKED_AMOUNT ,0) BOOKED_AMOUNT
FROM
SXB S
FULL OUTER JOIN UBS U ON
S.ACCOUNT_NUMBER = U.AC_NO
AND
S.TRADEDATE = U.VALUE_DT
UNION ALL
SELECT
BOOKING_DATE TRADEDATE,
'SAXO RECON' ACCOUNT_NAME,
SUM((Case when DR_CR_INDICATOR = 'D' then AMOUNT*-1 when DR_CR_INDICATOR = 'C' then AMOUNT end)) BOOKING_AMOUNT,
EXTERNAL_ACCOUNT ACCOUNT_NUMBER,
'Matched - ' ||A.MATCH_INDICATOR AS DEBIT,
NULL AS CREDIT,
VALUE_DATE VALUE_DT,
NULL AS AC_NO,
0 AS BOOKED_AMOUNT
FROM
FCUBS.RETB_EXTERNAL_ENTRY A
WHERE A.EXTERNAL_ENTITY = 'SAXODKKKXXX'
AND A.EXTERNAL_ACCOUNT = '78600/COMMEUR'
group by
BOOKING_DATE ,
EXTERNAL_ACCOUNT ,
VALUE_DATE,
MATCH_INDICATOR
order by tradedate, account_name)
where tradedate = trunc (:sysdate1 - case to_char(:sysdate2, 'Dy') when 'Mon' then 3 else 1 end);
SYSDATE is a date datatype so oracle will always treat it as a DATE datatype. For a bind variable I'd do an explicit conversion using TO_DATE(:bind_var, 'FORMAT_MASK'). For example:
select
case TO_CHAR(TO_DATE(:sysdate2,'DD-MON-YYYY'), 'Dy') when 'Mon'
then 3
else 1
end from dual

Windowing Functions

I have the following events table.
key time_stamp geohash
k1 1 thred0y
k2 5 thred0v
k4 7 thre6rd
k3 9 thre6rg
k1 10 thred3t
k1 12 thred3u
k2 14 thred3s
Where I want to cluster the keys into groups if they fall with in 500mts range in 10 minutes of time interval.
I tried cross join them and
select a.key, b.key, a.geohash, b.geohash, a.time_stamp, b.time_stamp,
round(ST_Distance(ST_PointFromGeoHash(a.geohash, 4326), ST_PointFromGeoHash(b.geohash, 4326), true)) distance,
abs(round(extract(EPOCH from a.time_stamp - b.time_stamp)/60))
from t a, t b
where a.key <> b.key
and a.time_stamp between b.time_stamp - interval '10 min' and b.time_stamp + interval '10 min'
and ST_Distance(ST_PointFromGeoHash(a.geohash, 4326), ST_PointFromGeoHash(b.v, 4326), true) <= 500
and least(a.key, b.key) = a.key
order by a.time_stamp desc
However the query works good with small data and additionally the query only works if there are two distinct keys but not more than 2.
Any inputs on how to proceed further will be helpful.
I added some sample data for test, https://pastebin.com/iVD1WU4Y.
I found the solution by clustering keys within 60 minutes along with 1.2 km apart.
with x as (
select key, time_stamp, geo, prev_ts, geo_hash6,
count(case when prev_ts is null or prev_ts > 60 then 1 else null end) over(order by time_stamp) cluster_id
from (
select key, time_stamp, geo,
EXTRACT(EPOCH FROM time_stamp - lag(time_stamp) over(order by time_stamp)) prev_ts,
substring(geo, 1, 6) geo_hash6
from t
) a
order by cluster_id, geo_hash6, geo, time_stamp)
select x.cluster_id, x.key, x.geo_hash6, min(time_stamp) first_time, max(time_stamp) last_time
from x, (select cluster_id, geo_hash6, count(distinct key) num_uniques from x group by cluster_id, geo_hash6) y
where x.cluster_id = y.cluster_id and x.geo_hash6 = y.geo_hash6 and y.num_uniques > 2
group by x.cluster_id, x.geo_hash6, x.key
order by x.cluster_id, x.geo_hash6;
Any suggestions improving the solution is welcome.

Postgresql time between changes in boolean column

I have a table with four columns; ID, timestamp, event_name and changed_to(boolean).
I need to know how i can get the epoch between to 'timstamps' where 'changed_to' changes from true to false given the 'event_name' is e_DrvMotorStartUp, as an example.
id timestamp event_name changed_to
56682 2015-04-14 09:06:21.93022 e_DrvMotorStartUp t
56683 2015-04-14 09:06:24.928389 e_DrvMotorRun t
56684 2015-04-14 09:06:24.928389 e_DrvMotorStartUp f
Thanks.
What you need is a LATERAL JOIN.
SELECT e.*, e2.*, e."timestamp" - e2."timestamp"
FROM tbl AS e
,LATERAL (SELECT *
FROM tbl
WHERE tbl.event_name = e.event_name
AND e."timestamp" > tbl."timestamp"
AND tbl.changed_to = 't'
ORDER BY tbl."timestamp" DESC
LIMIT 1)
WHERE e.changed_to = 'f'

Netezza TO_CHAR Function not evaluating appropriately?

I am having issues with a query that, if ran with hard-coded dates, will insert the correct number of rows into a table (170K+). The issue is, when I try to automate it, by replacing the hard-coded dates with date functions, the query will then only insert a single row into a newly truncated table.
Example hard-coded date: '20150401'
Sample of that same date, using the date function:
TO_CHAR(last_day(add_months(now(), -3))+1, 'YYYYMMDD')
The above TO_CHAR function returns the desired result, when ran separately.
Here is a cleaned-up version of the query that results in a single row being inserted:
INSERT INTO SCHEMA.INSERT_TABLE(
SELECT TO_CHAR(now(), 'YYYYMM') TRAN_MONTH,
SUM(CASE WHEN B.DATE = TO_CHAR(last_day(add_months(now(), -3))+1, 'YYYYMMDD')
THEN 'Do stuff'
END) AS Stuff1,
SUM(CASE WHEN B.DATE = TO_CHAR(last_day(add_months(now(), -3))+1, 'YYYYMMDD')
THEN 'Do other stuff'
END) AS Stuff2,
SUM(CASE WHEN B.DATE = TO_CHAR(last_day(add_months(now(), -3))+1, 'YYYYMMDD')
THEN 'Do really weird stuff'
END) AS Stuff3,
SUM(CASE WHEN B.DATE = TO_CHAR(last_day(add_months(now(), -3))+1, 'YYYYMMDD')
THEN 'Do really really weird stuff'
END) AS Stuff4,
SUM(CASE WHEN A.CODE= 1
THEN 'Do ... '
END) AS Stuff5,
FROM
(SELECT Col1, Col2... FROM Table_A) A,
(SELECT Col1, Col2... FROM Table_B) B,
(SELECT Col1, Col2... FROM Table_C) C,
(SELECT Col1, Col2... FROM Table_D) D,
(SELECT Col1, Col2... FROM Table_E) E,
WHERE 'Conditions for A, B, C, D, and E are met'
AND B.DATE = TO_CHAR(last_day(add_months(now(), -3))+1,'YYYYMMDD')
GROUP BY All of the things
ORDER BY Something
);
I have done quite a bit of testing, and research, but I haven't found a possible cause as to why the amount of records returned would be so drastically different.
Thank you,
Justin
I think it's because you added a 1 to the character string resulting from your last_day function. Check your parentheses:
WHERE 'Conditions for A, B, C, D, and E are met'
AND B.DATE = TO_CHAR(last_day(add_months(now(), -3)+1)
If it isn't that (or you really do want to add 1 to a character string), then I'm going to go out on a limb and assume that B.DATE is a column of type date. If so, the reason it isn't comparing correctly is because you're relying on implicit conversion. Change your date filter to explicitly convert both sides.
WHERE 'Conditions for A, B, C, D, and E are met'
AND B.DATE::date = (last_day(add_months(now(), -3)+1)::date