history of changes to a column - sql

Please, consider the following scenario.
There are 2 tables: Core and History. Core looks as follows
HWDateStart
HWDateEnd
HWQueueID
2022-05-29 10:00:00
2022-06-04 00:45:00
WIN-S671INNTGRE.P00-K0-01
2022-05-29 10:00:00
2022-06-04 00:45:00
WIN-S671INNTGRE.P00-K0-02
History looks as follows
cntDatetime
cntSerialNumber
cntQueueName
cntQueueID
2022-05-29 02:28:00
SN01-01
p00-0000-01
WIN-S671INNTGRE.P00-K0-01
2022-05-29 02:28:00
SN02-01
p00-0000-02
WIN-S671INNTGRE.P00-K0-02
2022-06-04 00:26:00
SN02-02
p00-0000-02
WIN-S671INNTGRE.P00-K0-02
2022-06-04 00:26:00
SN01-01
p00-0000-01
WIN-S671INNTGRE.P00-K0-01
History holds a list of cntSerialNumber changes through the time
The following script that gives the user SerialNumber as it was at the beginning and the end of a certain period
SELECT Convert(date,[HWDateStart])
,Convert(date,[HWDateEnd])
,[HWQueueID]
, HS.cntSerialNumber
, HE.cntSerialNumber
FROM [watchdocstats].[dbo].[tblJT] SNTarget,
[watchdocstats].[dbo].[tblQueueByConfig] HS,
[watchdocstats].[dbo].[tblQueueByConfig] HE
WHERE
SNTarget.HWQueueID = HS.cntQueueID
AND SNTarget.HWQueueID = HE.cntQueueID
AND Convert(date,SNTarget.HWDateStart) = Convert(date,HS.cntDatetime)
AND Convert(date,SNTarget.HWDateEnd) = Convert(date,HE.cntDatetime)
But what if SerialNumber has been changed more than once down the road
Is there a way to select a table that would show commaseparated SerialNumbers in cntSerialNumber column?
Smth, like
(Start)
(End)
HWQueueID
cntSNHist
2022-05-29
2022-06-04
WIN-S671INNTGRE.P00-K0-01
SN01-01
2022-05-29
2022-06-04
WIN-S671INNTGRE.P00-K0-02
SN02-01,SN02-02,SN02-03

Depending on your DBMS, you should be able to use the LISTAGG function along with a GROUP BY clause

Related

Merge Output - Capture Column Name Updated

I am merging a table and want to retain the previous value in a seperate table, along with the column that was updated.
I have got it working to retain the values, but want to know how I can retain the column name.
Existing table: tTable1
qID qUnits qDateTime
1001 4900 2022-09-13 12:00:00.000
1002 6800 2022-09-14 15:00:00.000
1003 7400 2022-09-14 13:00:00.000
Temp Table (holds updated values): #updateValues
qID qOriginalUnit qUpdatedUnit
1001 4900 8900
1002 6800 13400
1002 7400 16500
The code i'm using currently, to output what the existing value was, before the update;
DECLARE #auditRecords TABLE(qID INT, PreviousValue VARCHAR(100)
MERGE tTable1 AS TARGET
USING #updateValues AS SOURCE
ON (SOURCE.qID = TARGET.qID)
WHEN MATCHED
THEN UPDATE SET
TARGET.qUnits = SOURCE.qUpdatedUnit
OUTPUT SOURCE.qID, SOURCE.qOriginalUnit INTO #auditRecords(qID,PreviousValue);
I'd like to be able to include the column name that was updated, in this instance QUnits, so the output would look like the following;
qID PreviousValue UpdatedColumn
1001 4900 qUnits
1002 6800 qUnits
1003 7400 qUnits
In this particular example, where you are only updating a single column, you can just hardcode the column name in the OUTPUT results:
OUTPUT SOURCE.qID,
SOURCE.qOriginalUnit,
'qUnits'
INTO #auditRecords(qID, PreviousValue, UpdatedColumn)
If you are updating multiple columns it becomes a bit more cumbersome as you need to compare values column by column.
You could also move this into an UPDATE TRIGGER in which you have access to the results of the COLUMNS_UPDATED function.

How can I get the row of data for each Policy where the RecentChat has happened between the StartTerm and EndTerm?

My data looks like this
**Policy RecentContact StartTerm EndTerm**
Pol234 12/08/2020 05/06/2020 07/09/2020
Pol234 12/08/2020 01/01/2021 08/08/2021
Pol234 12/08/2020 01/01/2020 04/06/2020
Pol567 15/01/2021 15/01/2021 01/12/2021
Pol567 15/01/2021 14/01/2020 14/01/2021
I have multiple rows of data where Policy and RecentContact are the same however the StartTerm and EndTerm are different. How can I get the row of data for each Policy where the RecentChat has happened between the StartTerm and EndTerm?
So for Pol234 I'd want to only pull row 1 and for Pol567 I'd want to pull row 4
You would use between:
select t.*
from t
where RecentContact between starttem and endterm;
This assumes that the three columns are stored as dates and not strings. If they are strings, fix the data model!

I'm looking to find an average difference between a series of 2 rows same column in SQL

So I've looked through a lot of questions about subtraction and all that for SQL but haven't found the exact same use.
I'm using a single table and trying to find an average response time between two people talking on my site. Here's the data sample:
id created_at conversation_id sender_id receiver_id
307165 2017-05-03 20:03:27 96557 24 1755
307166 2017-05-03 20:04:22 96557 1755 24
303130 2017-04-20 18:03:53 102458 2518 4475
302671 2017-04-18 20:11:20 102505 3100 1079
302670 2017-04-18 20:09:38 103014 3100 2676
350570 2017-09-18 20:59:56 103496 5453 929
290458 2017-02-16 13:38:47 103575 2841 2282
300001 2017-04-08 16:42:16 104159 2740 1689
304204 2017-04-24 17:31:25 104531 5963 1118
284873 2017-01-12 22:33:19 104712 3657 3967
284872 2017-01-12 22:31:38 104712 3967 3657
What I want is to find an Average Response Time based on the conversation_id
Hmmm . . . You can get the "response" for a given row by getting the next row between the two conversers. The rest is getting the average -- which is database dependent.
Something like this:
select avg(next_created_at - created_at) -- exact syntax depends on the database
from (select m.*,
(select min(m2.created_at)
from messages m2
where m2.sender_id = m.receiver_id and m.sender_id = m2.receiver_id and
m2.conversation_id = m.conversation_id and
m2.created_at > m.created_at
) next_created_at
from messages m
) mm
where next_created_at is not null;
A CTE will take care of bringing the conversation start and end into the same row.
Then use DATEDIFF to compute the response time, and average it.
Assumes there are only ever two entries per conversation (ignores others with 1 or more than 2).
WITH X AS (
SELECT conversation_id, MIN(created_at) AS convstart, MAX(created_at) AS convend
FROM theTable
GROUP BY conversation_id
HAVING COUNT(*) = 2
)
SELECT AVG(DATEDIFF(second,convstart,convend)) AS AvgResponse
FROM X

Create column based on grouping other values

I have difficulties formulating my issue.
I have a view which brings these results. There's a need to add a column to the view, which will pair up round-trip flights with identical number.
Flt_No From_Airport To_Airport Dep_Date RequiredResult
124 |LCA |CDG |10/19/14 5:00 1
125 |CDG |LCA |10/19/14 10:00 1
197 |LCA |BCN |10/4/12 5:00 2
198 |BCN |LCA |10/4/12 11:00 2
501 |LCA |HER |15/8/12 12:05 3
502 |HER |LCA |15/8/12 15:15 3
I.e. flight 124 is going from Larnaca to CDG, and flight 125 is going back from CDG to Larnaca - they both have to have the same identifier.
Round-trip flights will always have following flight numbers.
I have a bunch of conditions which I won't write now.
Omitting hours is not an option, they're important.
I was thinking dense_rank() but I don't know how to create one identifier for 2 flights with different numbers, please help.
If your data is similar to the sample data posted, then the following query should give the required result:
SELECT *,
DENSE_RANK() OVER (ORDER BY CASE
WHEN From_Airport < To_Airport THEN From_Airport
ELSE To_Airport
END)
FROM mytable
Join conditions are not limited to simple equality. Assuming {Flight No, Departure, Destination} is unique on any one day, then a self join should do it:
select whatever
from flights outbound
inner join flights inbound on outbound.flt_no+1 = inbound.flt_no
and cast(outbound.dep_date, date)
= cast(inbound.dep_date, date)
and outbound.From_Airport = inbound.To_Airport
and outbound.To_Airpott = inbound.From_Ariport

Find value based on same row values

I have a database with a table (tblPersonnel) that is populated with following data.
Name_Personnel VesselName SailoutDate Time_transfer Direction
JB Flight 2 3/03/2016 10:38:00 UP
MH Flight 2 3/03/2016 10:38:00 UP
RS Flight 2 3/03/2016 10:38:00 UP
JB Flight 2 3/03/2016 11:40:00 DOWN
MH Flight 2 3/03/2016 11:40:00 DOWN
RS Flight 2 3/03/2016 11:40:00 DOWN
I need to query the total time for all personnel between the "UP" and "DOWN" time.
I'd like to come with a output like this.
Name_Personnel VesselName SailoutDate Time_transfer_UP Time_transfer_DOWN Total_time
JB Flight 2 3/03/2016 10:38:00 11:40:00 01:02
MH Flight 2 3/03/2016 10:14:00 11:49:00 01:35
RS Flight 2 3/03/2016 10:36:00 11:53:00 01:17
The Name_personnel, vesselname and sailoutdate always have an "UP" and a "Down" value. So these can be used to search matching rows.
How can I do this?
You can use conditional aggregation. The challenge is the total time. If you can live with total minutes, then it is pretty easy:
select Name_Personnel, VesselName, SailoutDate,
max(iif(direction = 'UP', time_transfer, NULL)) as time_transfer_up,
max(iif(direction = 'DOWN', time_transfer, NULL)) as time_transfer_down,
datediff("minute",
max(iif(direction = 'UP', time_transfer, NULL))
max(iif(direction = 'DOWN', time_transfer, NULL))
) as minutes_diff
from tblPersonnel
group by Name_Personnel, VesselName, SailoutDate;
Thanks,
Both answers worked fine, although I saw different output, as not all data was completely correctly filled.
My final query became this.
SELECT DateDiff("n",Max(IIf([direction]='UP',[time_transfer],Null)),Max(IIf([direction]='DOWN',[time_transfer],Null))) AS minutes_diff, tblPersons.Name_Personnel, tblPersons.VesselName, tblPersons.SailoutDate, tblPersons.Type, tblPersons.VesselName, Max(IIf([direction]='up',[time_transfer],Null)) AS Time_transfer_UP, Max(IIf([direction]='Down',[time_transfer],Null)) AS Time_tranfer_DOWN
FROM tblPersons, QRY_Numberofsailingdays
GROUP BY tblPersons.Name_Personnel, tblPersons.SailoutDate, tblPersons.Type, tblPersons.VesselName, tblPersons.VesselName
HAVING (((tblPersons.SailoutDate) Between [Forms]![FRM_Working_Time_Personnel]![TXT_startdate] And [Forms]![FRM_Working_Time_Personnel]![TXT_enddate]));