DB2 Get latest modified and previous value from audit table - sql

I have a audit table, i am trying to get the current and previous value for a column(rank) with audit timestamp information. I would like to get the timestamp when the value was changed. E.g:
For id = 1, rank was latest changed from 3 to 5 on 13-05-2021 14:10 by userid = 2.
I have written below query it gives the current and previous modified value but it gives the latest date and userid (17-05-2021 20:00 and 2), because row_number is ordered by timestamp.
with v_rank as (
select * from (
select
id,
a.rank as current_rank,
b.rank as previous_rank,
a.log_timestamp,
a.log_username,
row_number() over(partition by a.id order by a.log_timestamp) as rnum
from
user a
inner join user b on a.id = b.id and a.log_timestamp > b.timestamp
where
a.rank != b.rank
order by a.log_timestamp, b.timestamp
) where rnum = 1
)
select * from v_rank
Any suggestion on how can i get the correct timestamp(13-05-2021 14:10) and userid(2).
Edit:
Rank can also be null, in that case i need to get the blank in query result.
Expected output:

You seem to want lag() with filtering:
select u.*
from (select u.*,
lag(rank) over (partition by id order by log_timestamp) as prev_rank
from user u
) u
where rank <> prev_rank;

Related

How can I obtain the minimum date for a value that is equal to the maximum date?

I am trying to obtain the minimum start date for a query, in which the value is equal to its maximum date. So far, I'm able to obtain the value in it's maximum date, but I can't seem to obtain the minimum date where that value remains the same.
Here is what I got so far and the query result:
select a.id, a.end_date, a.value
from database1 as a
inner join (
select id, max(end_date) as end_date
from database1
group by id
) as b on a.id = b.id and a.end_date = b.end_date
where value is not null
order by id, end_date
This result obtains the most recent record, but I'm looking to obtain the most minimum end date record where the value remains the same as the most recent.
In the following sample table, this is the record I'd like to obtain the record from the row where id = 3, as it has the minimum end date in which the value remains the same:
id
end_date
value
1
02/12/22
5
2
02/13/22
5
3
02/14/22
4
4
02/15/22
4
Another option that just approaches the problem somewhat as described for the sample data as shown - Get the value of the maximum date and then the minimum id row that has that value:
select top(1) t.*
from (
select top(1) Max(end_date)d, [value]
from t
group by [value]
order by d desc
)d
join t on t.[value] = d.[value]
order by t.id;
DB<>Fiddle
I'm most likely overthinking this as a Gaps & Island problem, but you can do:
select min(end_date) as first_date
from (
select *, sum(inc) over (order by end_date desc) as grp
from (
select *,
case when value <> lag(value) over (order by end_date desc) then 1 else 0 end as inc
from t
) x
) y
where grp = 0
Result:
first_date
----------
2022-02-14
See running example at SQL Fiddle.
with data as (
select *,
row_number() over (partition by value) as rn,
last_value(value) over (order by end_date) as lv
from T
)
select * from data
where value = lv and rn = 1
This isn't looking strictly for streaks of consecutive days. Any date that happened to have the same value as on final date would be in contention.

How do I use row_number in Hive query to get the latest user login?

I have this query which runs fine
The log table looks like this
reportid, timestamp, userid
SELECT
reportid,
b.email
FROM
logs
JOIN mongo.user b on a.userid = b.id
WHERE
a.dt >= date_sub(current_date, 14)
GROUP BY
reportid,
b.email
The result will be something like this
reportid, email
1, xxx#xxx.com
1, xxx1#xxx.com
However, I want the result to just be
reportid, email
1, xxx1#xxx.com
This is just based on the timestamp. I learned about row_number but when I added this
row_number() over (partition by userid order by timestamp desc)
I get this error
Error while compiling statement: FAILED: SemanticException Failed to breakup Windowing invocations into Groups. At least 1 group must only depend on input columns. Also check for circular dependencies. Underlying error: org.apache.hadoop.hive.ql.parse.SemanticException: Line 7:34 Expression not in GROUP BY key 'userid'
I just want the userid who accessed the report the latest based on the timestamp
If you are looking for the latest userid accessing the report,then sort the records by timestamp descending and get the first row using limit
select userid, b.email
from logs a
join mongo.user b on a.userid = b.id
where a.dt >= date_sub(current_date, 14)
sort by timestamp desc
limit 1
If you are looking to get the list of users and their latest timestamps of accessing the reports then you need to give row_number() column a name and use it to get the 1 row for each userid group.
select userid,email
from
(
select userid, b.email, row_number() over (partition by userid order by timestamp desc) as r_no
from logs a
join mongo.user b on a.userid = b.id
where a.dt >= date_sub(current_date, 14)
) t
where t.r_no=1

Select the Max date time for single User

I have a table like this,
Date User
15-06-2018 A
16-06-2018 A
15-06-2018 B
14-06-2018 C
16-06-2018 C
I want to get the output like this,
Date User
16-06-2018 A
15-06-2018 B
16-06-2018 C
I tried Select Max(date),User from Table group by User
Based on your comment, I assume you have duplicated results in those 80 columns when you group by them. Assuming so, here's one option using row_number to always return 1 row per user:
select *
from (
select *, row_number() over (partition by user order by date desc) rn
from yourtable
) t
where rn = 1
You can use correlation subquery :
select t.*
from table t
where date = (select max(t1.date)
from table t1
where t1.user = t.user
);
However, i would also recommend row_number() :
select top (1) with ties *
from table t
order by row_number() over (partition by user order by date desc);
You can also use a ranking function
SELECT User, Date
FROM
(
SELECT User, Date
, Row_id = Row_Number() OVER (Partition by User, ORDER BY User, Date desc)
FROM table
)q
WHERE Row_Id = 1
I would suggest you this
Select * from table t where exist
(Select 1 from
(Select user, max(date) as date from table) A
Where A.user = t.user and A.date = t.date )

SQL - pull unique name with the lastest date and lowest value

how do i get unique name with the latest date and lowest value.
Name date value
brad 1/2/10 1.1
brad 1/2/10 2.3
bob 1/6/10 1.0
brad 2/4/09 13.2
this query does not seem to work
SELECT distinct
A.[ViralLoadMemberID]
,B.LastName
,B.FirstName
,A.[Date]
,A.[vaule]
FROM [t].[dbo].[tblViralLoad] A
left join [dbo].[tblEnrollees] B on A.ViralLoadMemberID = B.MemberID
where
A.Date =
(
select MAX(Date)
from dbo.tblViralLoad
where ViralLoadMemberID = A.ViralLoadMemberID
and
( Date >= '07/01/2014'
and Date <= '12/3/2014' ) )
The idea is to use order by and fetch only one row. If you want the lowest value on the latest date, the standard SQL would be:
select t.*
from table t
order by desc desc, value asc
fetch first 1 row only;
For older versions of SQL Server, you would omit the last line and do select top 1 * . . .. For MySQL, the last line would be limit 1.
Fun with rank()
declare #t as table (name varchar(50),dte date,val decimal(18,10));
insert into #t(name,dte,val) values
('Dave','1/1/2015',1.0),
('Dave','1/3/2015',1.2),
('Dave','1/4/2015',1.5),
('Dave','1/10/2015',1.3),
('Dave','1/15/2015',1.2),
('Steve','1/11/2015',1.6),
('Steve','1/12/2015',1.1),
('Steve','1/15/2015',1.2),
('Bill','1/21/2015',1.9),
('Ted','1/1/2015',1.8),
('Ted','1/10/2015',1.0),
('Ted','1/12/2015',1.7)
-- This will show the lowest prices by each person
select name,dte,val from (select name,dte,val, rank() over (partition by name order by val) as r from #t) as data where r = 1
-- This will be users lowest price and the last day they sublitted a prices regurdless if it is the lowest
select name,max(dte) as [last Date] ,min(val) as [Lowest Value] from #t group by name
-- Who had the lowest price last regurdless if they have raised there price later.
select top(1) name,dte [last lowest quote],val from (select name,dte,val, rank() over (order by val) as r from #t) as data where r = 1 order by dte desc
-- what is the lowest price cueently quoted reguarless who quoted it
select top(1) name,dte [best active quote],val from (select name,dte,val, rank() over (partition by name order by dte desc) as r from #t) as data where r = 1 order by val

how do I query sql for a latest record date for each user

I have a table that is a collection entries as to when a user was logged on.
username, date, value
--------------------------
brad, 1/2/2010, 1.1
fred, 1/3/2010, 1.0
bob, 8/4/2009, 1.5
brad, 2/2/2010, 1.2
fred, 12/2/2009, 1.3
etc..
How do I create a query that would give me the latest date for each user?
Update: I forgot that I needed to have a value that goes along with the latest date.
This is the simple old school approach that works with almost any db engine, but you have to watch out for duplicates:
select t.username, t.date, t.value
from MyTable t
inner join (
select username, max(date) as MaxDate
from MyTable
group by username
) tm on t.username = tm.username and t.date = tm.MaxDate
Using window functions will avoid any possible issues with duplicate records due to duplicate date values, so if your db engine allows it you can do something like this:
select x.username, x.date, x.value
from (
select username, date, value,
row_number() over (partition by username order by date desc) as _rn
from MyTable
) x
where x._rn = 1
Using window functions (works in Oracle, Postgres 8.4, SQL Server 2005, DB2, Sybase, Firebird 3.0, MariaDB 10.3)
select * from (
select
username,
date,
value,
row_number() over(partition by username order by date desc) as rn
from
yourtable
) t
where t.rn = 1
I see most of the developers use an inline query without considering its impact on huge data.
Simply, you can achieve this by:
SELECT a.username, a.date, a.value
FROM myTable a
LEFT OUTER JOIN myTable b
ON a.username = b.username
AND a.date < b.date
WHERE b.username IS NULL
ORDER BY a.date desc;
From my experience the fastest way is to take each row for which there is no newer row in the table.
Another advantage is that the syntax used is very simple, and that the meaning of the query is rather easy to grasp (take all rows such that no newer row exists for the username being considered).
NOT EXISTS
SELECT username, value
FROM t
WHERE NOT EXISTS (
SELECT *
FROM t AS witness
WHERE witness.username = t.username AND witness.date > t.date
);
ROW_NUMBER
SELECT username, value
FROM (
SELECT username, value, row_number() OVER (PARTITION BY username ORDER BY date DESC) AS rn
FROM t
) t2
WHERE rn = 1
INNER JOIN
SELECT t.username, t.value
FROM t
INNER JOIN (
SELECT username, MAX(date) AS date
FROM t
GROUP BY username
) tm ON t.username = tm.username AND t.date = tm.date;
LEFT OUTER JOIN
SELECT username, value
FROM t
LEFT OUTER JOIN t AS w ON t.username = w.username AND t.date < w.date
WHERE w.username IS NULL
To get the whole row containing the max date for the user:
select username, date, value
from tablename where (username, date) in (
select username, max(date) as date
from tablename
group by username
)
SELECT *
FROM MyTable T1
WHERE date = (
SELECT max(date)
FROM MyTable T2
WHERE T1.username=T2.username
)
This one should give you the correct result for your edited question.
The sub-query makes sure to find only rows of the latest date, and the outer GROUP BY will take care of ties. When there are two entries for the same date for the same user, it will return the one with the highest value.
SELECT t.username, t.date, MAX( t.value ) value
FROM your_table t
JOIN (
SELECT username, MAX( date ) date
FROM your_table
GROUP BY username
) x ON ( x.username = t.username AND x.date = t.date )
GROUP BY t.username, t.date
If your database syntax supports it, then TOP 1 WITH TIES can be a lifesafer in combination with ROWNUMER.
With the example data you provided, use this query:
SELECT TOP 1 WITH TIES
username, date, value
FROM user_log_in_attempts
ORDER BY ROW_NUMBER() OVER (PARTITION BY username ORDER BY date DESC)
It yields:
username | date | value
-----------------------------
bob | 8/4/2009 | 1.5
brad | 2/2/2010 | 1.2
fred | 12/2/2009 | 1.3
Demo
How it works:
ROWNUMBER() OVER (PARTITION BY... ORDER BY...) For each username a list of rows is calculated from the youngest (rownumber=1) to the oldest (rownumber=high)
ORDER BY ROWNUMBER... sorts the youngest rows of each user to the top, followed by the second-youngest rows of each user, and so on
TOP 1 WITH TIES Because each user has a youngest row, those youngest rows are equal in the sense of the sorting criteria (all have rownumber=1). All those youngest rows will be returned.
Tested with SQL-Server.
SELECT DISTINCT Username, Dates,value
FROM TableName
WHERE Dates IN (SELECT MAX(Dates) FROM TableName GROUP BY Username)
Username Dates value
bob 2010-02-02 1.2
brad 2010-01-02 1.1
fred 2010-01-03 1.0
This is similar to one of the answers above, but in my opinion it is a lot simpler and tidier. Also, shows a good use for the cross apply statement. For SQL Server 2005 and above...
select
a.username,
a.date,
a.value,
from yourtable a
cross apply (select max(date) 'maxdate' from yourtable a1 where a.username=a1.username) b
where a.date=b.maxdate
You could also use analytical Rank Function
with temp as
(
select username, date, RANK() over (partition by username order by date desc) as rnk from t
)
select username, rnk from t where rnk = 1
SELECT MAX(DATE) AS dates
FROM assignment
JOIN paper_submission_detail ON assignment.PAPER_SUB_ID =
paper_submission_detail.PAPER_SUB_ID
SELECT Username, date, value
from MyTable mt
inner join (select username, max(date) date
from MyTable
group by username) sub
on sub.username = mt.username
and sub.date = mt.date
Would address the updated problem. It might not work so well on large tables, even with good indexing.
SELECT *
FROM ReportStatus c
inner join ( SELECT
MAX(Date) AS MaxDate
FROM ReportStatus ) m
on c.date = m.maxdate
For Oracle sorts the result set in descending order and takes the first record, so you will get the latest record:
select * from mytable
where rownum = 1
order by date desc
SELECT t1.username, t1.date, value
FROM MyTable as t1
INNER JOIN (SELECT username, MAX(date)
FROM MyTable
GROUP BY username) as t2 ON t2.username = t1.username AND t2.date = t1.date
Select * from table1 where lastest_date=(select Max(latest_date) from table1 where user=yourUserName)
Inner Query will return the latest date for the current user, Outer query will pull all the data according to the inner query result.
I used this way to take the last record for each user that I have on my table.
It was a query to get last location for salesman as per recent time detected on PDA devices.
CREATE FUNCTION dbo.UsersLocation()
RETURNS TABLE
AS
RETURN
Select GS.UserID, MAX(GS.UTCDateTime) 'LastDate'
From USERGPS GS
where year(GS.UTCDateTime) = YEAR(GETDATE())
Group By GS.UserID
GO
select gs.UserID, sl.LastDate, gs.Latitude , gs.Longitude
from USERGPS gs
inner join USER s on gs.SalesManNo = s.SalesmanNo
inner join dbo.UsersLocation() sl on gs.UserID= sl.UserID and gs.UTCDateTime = sl.LastDate
order by LastDate desc
My small compilation
self join better than nested select
but group by doesn't give you primary key which is preferable for join
this key can be given by partition by in conjunction with first_value (docs)
So, here is a query:
select
t.*
from
Table t inner join (
select distinct first_value(ID) over(partition by GroupColumn order by DateColumn desc) as ID
from Table
where FilterColumn = 'value'
) j on t.ID = j.ID
Pros:
Filter data with where statement using any column
select any columns from filtered rows
Cons:
Need MS SQL Server starting with 2012.
I did somewhat for my application as it:
Below is the query:
select distinct i.userId,i.statusCheck, l.userName from internetstatus
as i inner join login as l on i.userID=l.userID
where nowtime in((select max(nowtime) from InternetStatus group by userID));
Here's one way to return only the most recent record for each user in SQL Server:
WITH CTE AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date DESC) AS rn
FROM your_table
)
SELECT *
FROM CTE
WHERE rn = 1;
This uses a common table expression (CTE) to assign a unique rn (row number) to each record for each user, based on the user_id and sorted in descending order by date. The final query then selects only the records with rn equal to 1, which represents the most recent record for each user.
SELECT * FROM TABEL1 WHERE DATE= (SELECT MAX(CREATED_DATE) FROM TABEL1)
You would use aggregate function MAX and GROUP BY
SELECT username, MAX(date), value FROM tablename GROUP BY username, value