SQL specific column update time in system-versioned temporal table - sql

Is there any easy way to get information about the last update date of a selected column using system-versioned temporal table?
I have a table with columns A, B, C, each of them is updated randomly and separately, but I am interested in whether it is able to easily extract the date of the last update in column B.
I added a photo for the sake of simplicity, I need to extract information when there was the last change in the value in column A (in the photo I marked the last change in this column)

Use the lag() window function to look for changes in B, summarize that set to find max(StartTime), and use that in a Where filter to select your latest record.
Select * From history.table
Where StartTime=(Select max(StartTime) from
( Select *,
B<>lag(B) Over (Order By StartTime) as B_Changed
From history.table
)
Where B_Changed
)

I was able to find a simple solution that solves each case, below is the solution
SELECT TOP (1) * FROM (
SELECT
ID,
A,
LAG(A) OVER(PARTITION BY ID ORDER BY StartTime) AS PreviousA,
UserID,
StartTime
FROM dbo.Table FOR SYSTEM_TIME ALL
)t
WHERE t.A <> t.PreviousA
ORDER BY t.StartTime desc
The query returns the last modification in the column, if there was no modification in the table or only another column was modified, it correctly returns an empty row informing that there were no changes. Maybe someone will need it in the future. Thank you for your help.

Related

SQL Server : verify that two columns are in same sort order

I have a table with an ID and a date column. It's possible (likely) that when a new record is created, it gets the next larger ID and the current datetime. So if I were to sort by date or I were to sort by ID, the resulting data set would be in the same order.
How do I write a SQL query to verify this?
It's also possible that an older record is modified and the date is updated. In that case, the records would not be in the same sort order. I don't think this happens.
I'm trying to move the data to another location, and if I know that there are no modified records, that makes it a lot simpler.
I'm pretty sure I only need to query those two columns: ID, RecordDate. Other links indicate I should be able to use LAG, but I'm getting an error that it isn't a built-in function name.
In other words, both https://dba.stackexchange.com/questions/42985/running-total-to-the-previous-row and Is there a way to access the "previous row" value in a SELECT statement? should help, but I'm still not able to make that work for what I want.
If you cannot use window functions, you can use a correlated subquery and EXISTS.
SELECT *
FROM elbat t1
WHERE EXISTS (SELECT *
FROM elbat t2
WHERE t2.id < t1.id
AND t2.recorddate > t1.recorddate);
It'll select all records where another record with a lower ID and a greater timestamp exists. If the result is empty you know that no such record exists and the data is like you want it to be.
Maybe you want to restrict it a bit more by using t2.recorddate >= t1.recorddate instead of t2.recorddate > t1.recorddate. I'm not sure how you want it.
Use this:
SELECT ID, RecordDate FROM tablename t
WHERE
(SELECT COUNT(*) FROM tablename WHERE tablename.ID < t.ID)
<>
(SELECT COUNT(*) FROM tablename WHERE tablename.RecordDate < t.RecordDate);
It counts for each row, how many rows have id less than the row's id and
how many rows have RecordDate less than the row's RecordDate.
If these counters are not equal then it outputs this row.
The result is all the rows that would not be in the same position after sorting by ID and RecordDate
One method uses window functions:
select count(*)
from (select t.*,
row_number() over (order by id) as seqnum_id,
row_number() over (order by date, id) as seqnum_date
from t
) t
where seqnum_id <> seqnum_date;
When the count is zero, then the two columns have the same ordering. Note that the second order by includes id. Two rows could have the same date. This makes the sort stable, so the comparison is valid even when date has duplicates.
the above solutions are all good but if both dates and ids are in increment then this should also work
select modifiedid=t2.id from
yourtable t1 join yourtable t2
on t1.id=t2.id+1 and t1.recordDate<t2.recordDate

SQL Eliminate Duplicates with NO ID

I have a table with the following Columns...
Node, Date_Time, Market, Price
I would like to delete all but 1 record for each Node, Date time.
SELECT Node, Date_Time, MAX(Price)
FROM Hourly_Data
Group BY Node, Date_Time
That gets the results I would like to see but cant figure out how to remove the other records.
Note - There is no ID for this table
Here are steps that are rather workaround than a simple one-command which will work in any relational database:
Create new table that looks just like the one you already have
Insert the data computed by your group-by query to newly created table
Drop the old table
Rename new table to the name the old one used to have
Just remember that locking takes place and you need to have some maintenance time to perform this action.
There are simpler ways to achieve this, but they are DBMS specific.
here is an easy sql-server method that creates a Row Number within a cte and deletes from it. I believe this method also works for most RDBMS that support window functions and Common Table Expressions.
;WITH cte AS (
SELECT
*
,RowNum = ROW_NUMBER() OVER (PARTITION BY Node, Date_Time ORDER BY Price DESC)
FROM
Hourly_Data
)
DELETE
FROM
cte
WHERE
RowNum > 1

SQL query to select one of multiple rows from a table

I have a table which contains more than one row for a particular value . Here is the table structure:
NAME,NUMBER,STATUS,DESC,START_DATE,END_DATE
A,3,X,DetailsOfX,13-10-15,13-10-15
A,2,Y,DetailsOfY,13-10-15,13-10-15
A,2,Z,DetailsOfZ,13-10-15,13-10-15
A,1,X,DetailsOfX,12-10-15,12-10-15
The output i need is i.e.
A,3,X,DetailsOfX,13-10-15,13-10-15
A,2,Y,DetailsOfY-DetailsofZ,13-10-15,13-10-15
A,1,X,DetailsOfX,12-10-15,12-10-15
So basically i want to select one of two or more rows from a table with data from columns from both the rows (in bold above). The query below i tried using JOIN returns 4 rows.
SELECT A.NAME,A.NUMBER,B.STATUS,A.DESC||"-"||B.DESC,A.START_DATE,A.END_DATE
FROM TABLE A
JOIN (SELECT NUMBER,STATUS,DESC,START_DATE,END_DATE FROM TABLE WHERE NAME='A') B
ON A.NAME=B.NAME AND
A.NUMBER=B.NUMBER
Can somebody help me with the query that would work.
Thanks
If you are using IBM i 7.1 (formerly known as OS/400), you should be able do this with two tricks: hierarchical queries, and XML functions.
See my tutorial under Q: SQL concatenate strings which explains how to do this on DB2 for i in order to merge the descriptions.
GROUP BY any fields by which you would want rows combined into one, but all other columns must be the result of an aggregate function. So for example, if you want one row per name, number, but have various values for Status, StartDate, EndDate, then you will need to say something like min(Status), min(StartDate), max(EndDate). Is the minimum status code actually the one you want to report?
If your OS is at version 6.1, you may still be able to use a conventional recursive query (or under v5r4), but you might need an addtional CTE (or two?) to concatenate the descriptions.
You need to use GROUP BY and FOR XML PATH:
SELECT
X.NAME, X.NUMBER, X.STATUS,
STUFF((
SELECT '-' + [Desc] AS Desc
FROM YourTable Y
WHERE Y.ID = X.ID
FOR XML PATH(''),TYPE),1,1,'') AS DescValues,
StartDate,
EndDate
FROM YourTable X
GROUP BY Name, Number, Status, StartDate, EndDate
This is assuming you want separate rows for any differences in name, number, status, start date, or end date.
Also, this is assuming SQL Server.

Computed column formula ( yyMMdd## )

I need a computed column formula that gives me this yyMMdd##.
I have an identity column (DataID) and a date column (DataDate).
This what I have so far.
(((right(CONVERT([varchar](4),datepart(year,[DataDate]),0),(2))+
right(CONVERT([varchar](4),datepart(month,[DataDate]),0),(2)))+
right(CONVERT([varchar](4),datepart(day,[DataDate]),0),(2)))+
right('00'+CONVERT([varchar](2),[DataID],0),(2)))
And this gives me:
12111201
12111202
12111303
12111304
12111405
12111406
12111407
12111508
What I want is:
12111201
12111202
12111301
12111302
12111401
12111402
12111403
12111501
I'm assuming you want to have a sequence starting at 1 for each date - right? If not: please explain what you really want / need.
You won't be able to do this with a IDENTITY column and a computed column specification. An IDENTITY column returns constantly increasing numbers.
What you could do is not store those values on disk - but instead use CTE and the ROW_NUMBER() OVER (PARTITION BY....) construct to create those numbers on the fly - whenever you need to select them. Or have a job that sets those values based on such a CTE on a regular basis (e.g. once every hour or so).
That CTE might look something like this - again, assuming that DataDate is indeed of type DATE (and not DATETIME or something like that) :
;WITH CTE AS
(
SELECT
DataID, DataDate,
RowNum = ROW_NUMBER() OVER (PARTITION BY DataDate ORDER BY DataID)
FROM
dbo.YourTable
)
SELECT
DataID, DataDate, RowNum
FROM
CTE

Deleting Duplicate Records from a Table

I Have a table called Table1 which has 48 records. Out of which only 24 should be there in that table. For some reason I got duplicate records inserted into it. How do I delete the duplicate records from that table.
Here's something you might try if SQL Server version is 2005 or later.
WITH cte AS
(
SELECT {list-of-columns-in-table},
row_number() over (PARTITION BY {list-of-key-columns} ORDER BY {rule-to-determine-row-to-keep}) as sequence
FROM myTable
)
DELETE FROM cte
WHERE sequence > 1
This uses a common table expression (CTE) and adds a sequence column. {list-of-columns-in-table} is just as it states. Not all columns are needed, but I won't explain here.
The {list-of-key-columns] is the columns that you use to define what is a duplicate.
{rule-to-determine-row-to-keep} is a sequence so that the first row is the row to keep. For example, if you want to keep the oldest row, you would use a date column for sequence.
Here's an example of the query with real columns.
WITH cte AS
(
SELECT ID, CourseName, DateAdded,
row_number() over (PARTITION BY CourseName ORDER BY DateAdded) as sequence
FROM Courses
)
DELETE FROM cte
WHERE sequence > 1
This example removes duplicate rows based on the CoursName value and keeps the oldest basesd on the DateAdded value.
http://support.microsoft.com/kb/139444
This section is the key. The primary point you should take away. ;)
This article discusses how to locate
and remove duplicate primary keys from
a table. However, you should closely
examine the process which allowed the
duplicates to happen in order to
prevent a recurrence.
Identify your records by grouping data by your logical keys, since you obviously haven't defined them, and applying a HAVING COUNT(*) > 1 statement at the end. The article goes into this in depth.
This is an easier way
Select * Into #TempTable FROM YourTable
Truncate Table YourTable
Insert into YourTable Select Distinct * from #TempTable
Drop Table #TempTable