building Snapshot table from audit trail in ms sql - sql-server

I'm faced with the same problem related here Building a snapshot table from audit records. The code bellow solve partially my problem
Select * into #temp from (
SELECT Audit.PrimaryKeyValue as ID,Audit.FieldName,OldValue FROM audit left JOIN (
SELECT Audit.FieldName,Audit.PrimaryKeyValue, MAX(UpdateDate) AS dateadded FROM audit GROUP BY FieldName,PrimaryKeyValue
) maxtimestamp ON audit.FieldName = maxtimestamp.FieldName AND audit.updateDate = maxtimestamp.dateadded
where PrimaryKeyField='Id' and cast(UpdateDate as date)<=#Data) src
pivot(
max(src.OldValue)
for FieldName in (Centrala,ID_Grup,Pi, Ci, Pmt, Pneta, Rpp, Pd, UD, Suport1, Suport2, Suport3, Stare,
Motiv, Observatii, Comentarii, Un, Data_ADD, Modified_Date, Scada, Fuel_base)
) piv;
How to obtain a snapshot of a table based on records from audit trail table at a given moment.
By copying actual table in a temp one and by updating values from it based on content of audit is a solution?
My English is poor!
Thanks!!!
The structure of the primary table is:
[ID] [int] IDENTITY(1,1) NOT NULL,
[Centrala] [int] NOT NULL,
[ID_grup] [nvarchar](50) NULL,
[Pi] [float] NULL,
[Ci] [float] NULL,
[Pmt] [float] NULL,
[Pneta] [float] NULL,
[Rpp] [float] NULL,
[Pd] [float] NULL,
[UD] [nvarchar](50) NULL,
[Suport1] [nvarchar](255) NULL,
[Suport2] [nvarchar](255) NULL,
[Suport3] [nvarchar](255) NULL,
[Stare] [int] NULL,
[Motiv] [nvarchar](max) NULL,
[Observatii] [nvarchar](max) NULL,
[Comentarii] [nvarchar](max) NULL,
[Un] [varchar](10) NULL,
[Data_ADD] [date] NULL,
[Modified_Date] [date] NULL,
[Scada] [nvarchar](100) NULL,
[Fuel_base] [nvarchar](255) NULL,
and the structure of the Audit table is:
[AuditID] [int] IDENTITY(1,1) NOT NULL,
[Type] [char](1) NULL,
[TableName] [varchar](128) NULL,
[PrimaryKeyField] [varchar](1000) NULL,
[PrimaryKeyValue] [varchar](1000) NULL,
[FieldName] [varchar](128) NULL,
[OldValue] [varchar](1000) NULL,
[NewValue] [varchar](1000) NULL,
[UpdateDate] [datetime] NULL,
[UserName] [varchar](128) NULL
Users can modify values in primary table inclusive deleting entire rows and Audit table catch all modifications.I have to do a report with content of primary table at certain date back in time. I think the columns name in Audit table are expressive, Type has three values 'U','I','D' for update,insert and delete actions. Another problem is that if the Audit table contain modification for rows in primary table and the date for snapshot is lower than updateDate in Audit then I have to choose OldValue else NewValue. It is correct?
Thank you #Nick.McDermaid for your reply!!

I found an extremely ugly solution, but I think it works fine by now
Declare #data date
select #data='2016.02.2'
select * into #Grup1 from Grupuri
--Apply to actual values most oldest values from Audit
select * into #temp from (
SELECT
Audit.PrimaryKeyValue as ID,Audit.FieldName,OldValue
FROM audit inner JOIN (
SELECT Audit.FieldName,Audit.PrimaryKeyValue, min(UpdateDate) AS dateadded FROM audit GROUP BY FieldName,PrimaryKeyValue
) maxtimestamp ON audit.FieldName = maxtimestamp.FieldName AND audit.updateDate = maxtimestamp.dateadded
where PrimaryKeyField='Id' and TableName='Grupuri' ) src
pivot(
max(src.OldValue)
for FieldName in (Centrala,ID_Grup,Pi, Ci, Pmt, Pneta, Rpp, Pd, UD, Suport1, Suport2, Suport3, Stare,
Motiv, Observatii, Comentarii, Un, Data_ADD, Modified_Date, Scada, Fuel_base)
) piv;
UPDATE #Grup1 SET Pi= (case When b.Pi is not null then b.Pi else #Grup1.Pi end),
Ci=case When b.Ci is not null then b.Ci else #Grup1.Ci end,
Pmt=case When b.Pmt is not null then b.Pmt else #Grup1.Pmt end,
Pneta=case When b.Pneta is not null then b.Pneta else #Grup1.Pneta end,
Rpp=case When b.Rpp is not null then b.Rpp else #Grup1.Rpp end,
Pd=case When b.Pd is not null then b.Pd else #Grup1.Pd end,
UD=case When b.Ud is not null then b.Ud else NULL end,
Suport1=case When b.Suport1 is not null then b.Suport1 else #Grup1.Suport1 end,
Suport2=case When b.Suport2 is not null then b.Suport2 else #Grup1.Suport2 end,
Suport3=case When b.Suport3 is not null then b.Suport3 else #Grup1.Suport3 end,
Stare=case When b.Stare is not null then b.Stare else #Grup1.Stare end,
Motiv=case When b.Motiv is not null then b.Motiv else #Grup1.Motiv end,
Observatii=case When b.Observatii is not null then b.Observatii else #Grup1.Observatii end,
Comentarii=case When b.Comentarii is not null then b.Comentarii else #Grup1.Comentarii end,
Un=case When b.Un is not null then b.Un else #Grup1.Un end,
Scada= case When b.Scada is not null then b.Scada else #Grup1.Scada end,
Fuel_base=case When b.Fuel_base is not null then b.Fuel_base else #Grup1.Fuel_base end
FROM #temp b WHERE #Grup1.id = b.id
--Apply new values updated up to #data
select * into #temp1 from (
SELECT
Audit.PrimaryKeyValue as ID,Audit.FieldName,NewValue
FROM audit left JOIN (
SELECT Audit.FieldName,Audit.PrimaryKeyValue, MAX(UpdateDate) AS dateadded FROM audit GROUP BY FieldName,PrimaryKeyValue
) maxtimestamp ON audit.FieldName = maxtimestamp.FieldName AND audit.updateDate = maxtimestamp.dateadded
where PrimaryKeyField='Id' and TableName='Grupuri'
and cast(UpdateDate as date) <=#Data) src
pivot(
max(src.NewValue)
for FieldName in (Centrala,ID_Grup,Pi, Ci, Pmt, Pneta, Rpp, Pd, UD, Suport1, Suport2, Suport3, Stare,
Motiv, Observatii, Comentarii, Un, Data_ADD, Modified_Date, Scada, Fuel_base)) piv;
UPDATE #Grup1 SET Pi= (case When b.Pi is not null then b.Pi else #Grup1.Pi end),
Ci=case When b.Ci is not null then b.Ci else #Grup1.Ci end,
Pmt=case When b.Pmt is not null then b.Pmt else #Grup1.Pmt end,
Pneta=case When b.Pneta is not null then b.Pneta else #Grup1.Pneta end,
Rpp=case When b.Rpp is not null then b.Rpp else #Grup1.Rpp end,
Pd=case When b.Pd is not null then b.Pd else #Grup1.Pd end,
UD=case When b.Ud is not null then b.Ud else '-' end,
Suport1=case When b.Suport1 is not null then b.Suport1 else #Grup1.Suport1 end,
Suport2=case When b.Suport2 is not null then b.Suport2 else #Grup1.Suport2 end,
Suport3=case When b.Suport3 is not null then b.Suport3 else #Grup1.Suport3 end,
Stare=case When b.Stare is not null then b.Stare else #Grup1.Stare end,
Motiv=case When b.Motiv is not null then b.Motiv else #Grup1.Motiv end,
Observatii=case When b.Observatii is not null then b.Observatii else #Grup1.Observatii end,
Comentarii=case When b.Comentarii is not null then b.Comentarii else #Grup1.Comentarii end,
Un=case When b.Un is not null then b.Un else #Grup1.Un end,
Scada= case When b.Scada is not null then b.Scada else #Grup1.Scada end,
Fuel_base=case When b.Fuel_base is not null then b.Fuel_base else #Grup1.Fuel_base end
FROM #temp1 b
WHERE #Grup1.id = b.id
Delete from #Grup1 where Data_ADD>#data
Select * from #Grup1
union
Select Old_ID,Centrala,ID_Grup,Pi, Ci, Pmt, Pneta, Rpp, Pd, UD, Suport1,Suport2, Suport3, Stare, Motiv,
Observatii, Comentarii, Un, Data_ADD, Modified_Date, Scada, Fuel_base From DeletedGrupuri where Deleted<=#Data and Old_ID is not null order by ID
drop table #temp
drop table #temp1
drop table #Grup1
If someone has a better solution or can improve this code please help me. Also I'm open to modify the design of table Audit to simplify this process.
Thank You!

Related

SQL column and datetime field not updating when using case

I have a dimension table updating from a stage table the 1st condition is that dim.dim_patient.Determination_Code=patient.Determination_Code after which other fields should update if there is a difference spotted in any of the fields below. It is not picking up the changes and when it does update the field it doesn't update the corresponding dss_update_time field.
drop table #dim_patient
CREATE TABLE #dim_patient(
[id] [varchar](20) NULL,
[episode] [int] NULL,
[Status_date] [date] NULL,
[Determination_Code] [varchar](40) NULL,
[Determination] [varchar](40) NULL,
[Status_Code] [varchar](40) NULL,
[Status_Value] [varchar](40) NULL,
[Days] [int] NULL,
[RTP_Date] [varchar](500) NULL,
[PET_Date] [date] NULL,
[dss_create_time] [datetime] NULL,
[dss_update_time] [datetime] NULL
)
----------------------------------------------------------------------------------------------------------
--primary keys are patid and episode number
--when Determination_Code is the same status_date should remain the same
--every other record should update
-------------------------------------------------------------------------------------------------
INSERT INTO #dim_patient VALUES
('1435','4','2019-08-01','2','Not Ready',NULL, NULL,NULL,NULL,NULL ,'2022-09-08 13:49:20.660','2022-09-08 13:49:20.660')
,('1435','5','2021-10-12','1','Ready ', 'Yes','Yes','50','2022-11-02',NULL,'2022-09-08 13:49:20.660','2022-11-08 17:04:39.233')
select * from #dim_patient
UPDATE [dim].[dim_patient] WITH ( TABLOCK )
SET
----Status_date = patient.Status_date
, Determination_Code = patient.Determination_Code
, Determination = patient.Determination
, Status_Code = patient.Status_Code
, Status_Value = patient.Status_Value
, Days = patient.Days
, RTP_Date = patient.RTP_Date
, PET_Date = patient.PET_Date
,dss_update_time =
---= getdate()
CASE WHEN dim_patient.Status_Code <> patient.Status_Code
THEN getdate()
WHEN dim_patient.Days <> patient.Days
THEN getdate()
WHEN dim_patient.RTP_Date <> patient.RTP_Date
THEN getdate()
WHEN dim_patient.PET_Date <> patient.PET_Date
THEN getdate()
ELSE dim.dim_patient.dss_update_time
END
FROM [stage].[patient] patient
JOIN dim.dim_patient
ON dim.dim_patient.id=patient.id
AND dim.dim_patient.episode=patient.episode
where dim.dim_patient.Determination_Code=patient.Determination_Code
AND (
( dim.dim_patient.Status_Code <>patient.Status_Code)
OR ( dim.dim_patient.Days <> patient.Days)
OR ( dim.dim_patient.RTP_Date <> patient.RTP_Date)
OR (dim.dim_patient.PET_Date <> patient.PET_Date)
)

After insert trigger doesn't work when using Inserted

I'm trying to write a trigger on my Employees table that should not allow the insertion of a new employee that has a hire date that is older than the hire date of his boss
CREATE TABLE [dbo].[Employees]
(
[EID] [int] IDENTITY(1,1) NOT NULL,
[Ename] [nvarchar](20) NOT NULL,
[Gender] [nvarchar](1) NOT NULL,
[IsMarried] [nvarchar](1) NOT NULL,
[Birthdate] [date] NOT NULL,
[HireDate] [date] NOT NULL,
[Salary] [float] NOT NULL,
[Notes] [nvarchar](200) NULL,
[NationalityID] [int] NULL,
[BossID] [int] NULL,
CONSTRAINT [PK_Employees]
PRIMARY KEY CLUSTERED ()
)
And here's the trigger code:
CREATE TRIGGER [dbo].[Trig_04]
ON [dbo].[Employees]
AFTER INSERT
AS
BEGIN
IF ((SELECT INSERTED.HireDate FROM INSERTED WHERE BossID <> EID) <
(SELECT Employees.HireDate FROM Employees
WHERE EID IN (SELECT Employees.BossID FROM Employees WHERE BossID <> EID)))
ROLLBACK
END
It executes normally (no errors) but it just doesn't work, but when I was using the employees table in the subquery instead of the inserted table, it was working normally. Does anyone have an answer for this?
You have to write triggers in SQL Server to handle the fact that INSERTED could contain multiple records. You cannot assume it will only be a single record. I think the following is what you are looking for:
if exists (
select 1
from Inserted I
where I.BossID <> I.EID
and I.HireDate < (select E.HireDate from Employees E where E.EID = I.BossID)
) begin
ROLLBACK;
end

How to avoid duplicate record while inserting data using user defined table type in SQL Server

I am trying to insert entire model in database using my .net application. I am using a user-defined table type.
This is my procedure and user-defined table; I am using SQL Server 2012.
CREATE TYPE [dbo].[TmpAccessRequest] AS TABLE
(
[RequestId] [int] NULL,
[RequesterID] [int] NULL,
[RequestType] [int] NULL,
[NextApprover] [int] NULL,
[RequestStatus] [varchar](100) NULL,
[Delegation] [int] NULL,
[CreatedOn] [date] NULL,
[CreatedBy] [varchar](100) NULL,
[Description] [varchar](max) NULL,
[IsSepecialRequest] [bit] NULL,
[DelegationDetailID] [int] NULL,
[IsActive] [bit] NULL,
[IsDeleted] [bit] NULL,
[ModifiedOn] [date] NULL
)
GO
CREATE PROCEDURE [dbo].[proc_SaveAccessRequest]
(#TmpAR TmpAccessRequest READONLY,
#IsUAMSRequest BIT,
#RequestID INT OUTPUT)
AS
BEGIN
INSERT INTO tblRequests (RequesterID, RequestType, NextApprover, RequestStatus,
Delegation, CreatedOn, CreatedBy, Description,
IsSepecialRequest, DelegationDetailID, IsActive, IsDeleted, ModifiedOn)
SELECT
RequesterID, RequestType, NextApprover, RequestStatus,
Delegation, CreatedOn, CreatedBy, Description,
IsSepecialRequest, DelegationDetailID, IsActive, IsDeleted, ModifiedOn
FROM
#TmpAR
SET #RequestID = SCOPE_IDENTITY()
--SET #RequestID=IDENT_CURRENT('tblRequests')
SELECT #RequestID
END
I want to check if duplicate data should not insert at the same time. So how can I do that with user-defined table type ?
Please find the changes done to your script to avoid inserting duplicate record. So i considered two columns data should be unique to avoid duplication for user understanding purpose
CREATE PROCEDURE [dbo].[proc_SaveAccessRequest]
(
#TmpAR TmpAccessRequest READONLY,
#IsUAMSRequest bit,
#RequestID int OUTPUT
)
AS
BEGIN
Insert into tblRequests
(
RequesterID
,RequestType
,NextApprover
,RequestStatus
,Delegation
,CreatedOn
,CreatedBy
,[Description]
,IsSepecialRequest
,DelegationDetailID
,IsActive
,IsDeleted
,ModifiedOn
)
SELECT
RequesterID
,RequestType
,NextApprover
,RequestStatus
,Delegation
,CreatedOn
,CreatedBy
,Description
,IsSepecialRequest
,DelegationDetailID
,IsActive
,IsDeleted
,ModifiedOn
FROM #TmpAR
WHERE NOT EXISTS ( SELECT 1
FROM tblRequests i
INNER JOIN #TmpAR o
ON i.RequesterID = o.RequesterID
AND i.RequestType = o.RequestType
AND i.NextApprover = o.NextApprover)
SELECT #RequestID = SCOPE_IDENTITY()
SELECT #RequestID
END

How to use multiple conditions in the where clause?

I'm a rookie to SQL Server. I'm using SQL Server 2008 R2. I have created two tables called adding_hanger and allot as follows
CREATE TABLE [dbo].[adding_hanger]
(
[End_Id] [bigint] IDENTITY(1,1) NOT NULL,
[Hanger_Location] [char](10) NOT NULL,
[Hanger_Capacity] [int] NOT NULL,
[Hanger_Id] AS ((CONVERT([varchar](100),substring([Hanger_Location],(1),(3)),0)+'101')+CONVERT([varchar](100),[End_Id],0)) PERSISTED NOT NULL,
[Manager_Name] [varchar](15) NOT NULL,
[Manager_Id] AS ((CONVERT([varchar](4),substring([Social_Security_No],(8),(4)),0)+'31')+CONVERT([varchar](100),[End_Id],0)) PERSISTED NOT NULL,
[Manager_Password] AS ((CONVERT([varchar](3),substring([Manager_Name],(1),(3)),0)+'#')+CONVERT([varchar](3),substring([Hanger_Location],(1),(3)),0)),
[Social_Security_No] [varchar](15) NOT NULL,
[Date_of_Birth] [datetime] NOT NULL,
[Gender] [varchar](8) NOT NULL,
[Mobile_No] [varchar](50) NOT NULL,
[Email_Address] [varchar](30) NOT NULL,
[House_No] [varchar](10) NOT NULL,
[Address_Line_1] [varchar](30) NOT NULL,
[Address_id] AS ((CONVERT([varchar](100),substring([City],(1),(3)),0)+'31')+CONVERT([varchar](100),[End_Id],0)) PERSISTED NOT NULL,
[City] [char](15) NOT NULL,
[State] [char](15) NOT NULL,
[Country] [char](15) NOT NULL,
[Pin_No] [int] NOT NULL
)
CREATE TABLE [dbo].[allot]
(
[Fromdate] [datetime] NULL,
[todate] [datetime] NULL,
[hangarlocation] [char](10) NULL,
[hangarno] [varchar](100) NULL,
[planeid] [varchar](100) NULL
)
How do I fetch the details of the available hangers that are not allotted within a given range of from and to dates given as input and also not exceeding the capacity of the hanger if allocated already?
First, your spelling of 'hangar' is inconsistent and should be addressed.
I'm assuming that the hangar capacity represents how many allots you can store in that hangar? If so, the hangar capacity might vary over a given timeframe as allots are added/removed (according to FromDate and ToDate), so your question isn't clear. If you want a hangar that definitely has a space free over a given period, I would use:
declare #from_date datetime, #end_date datetime, #i int
-- Set your start and end dates (replace the dates below with the correct ones)
set #from_date = '2016-07-01'
set #to_date = '2016-07-31'
set #i = 1 -- counter
-- Create a table of dates
select #from_date as date into #dates
while dateadd(day,#i,#from_date) <= #to_date
begin
insert into #dates values (dateadd(day,#i,#from_date))
end
-- Create table of date, hangar_id, hangar_capacity
select date, hangar_id, hangar_capacity
into #dates_by_hangar
from adding_hangar
inner join #dates on 1 = 1
-- Table showing hangars with at least one free space on every date in the range
select hangar_id from (
-- 2. Table showing hangar space free on each date
select a.*, a.hanger_capacity - isnull(b.allots_in_hangar,0) as hangar_space_free
from #dates_by_hangar a
left outer join (
-- 1. Table showing number of allots in each hangar on a given date
select date, hangar_no, sum(case when b.hangar_no is not null then 1 else 0 end) as allots_in_hangar
from #dates a
left outer join allot b
on a.date between b.fromdate and b.todate
group by date, hangar_no) b
on a.hangar_id = b.hangar_no
and a.date = b.date
)
group by hangar_no
having max(hangar_space_free) >= 1
SELECT ah.* from [adding_hanger] AS ah
INNER JOIN [allot] ON allot.hangarlocation = ah.Hanger_Location
WHERE (allot.Fromdate < <input_from_date> OR allot.todate > <input_to_date>)
OR (allot.Fromdate >= <input_from_date> AND allot.todate <= <input_to_date> AND ah.Hanger_Capacity <= <input_capacity>)

Is it possible to have a Case Statement in SQL Server Computed Column

I have a table with two date columns, both allow nulls.
if FileDate is not null then AsOfDate needs to equal (FileDate - Offset) where Offset is a integer column with a default of zero.
Here is my table def:
CREATE TABLE [import].[CMAR_GLXXXX](
[FileDate] [date] NULL,
[LoadDTM] [date] NULL,
[AsOfDate] [date] NULL,
[AccountNumber] [varchar](50) NOT NULL,
[CostCenter] [varchar](50) NOT NULL,
[OffSet] [int] default (0) NOT NULL,
[Value] [decimal](10,2) NOT NULL
) ON [PRIMARY]
I tried using the following:
(case when [File] IS NULL then NULL else dateadd(day,(-1 * [Offset]),[FileDate]) end)
Unfortunately the computed column error dialog doesn't display much help in troubleshooting the expression.
Any ideas?
A case expression is perfectly valid here but not needed for your desired output. Let sql server handle NULLS naturally.
dateadd(day,(-1 * x),[d1])
--EDIT--
To demonstrate here is a table definition. Seems this does exactly what you are looking for.
create table #Something
(
x int
, d1 datetime
, compCol as dateadd(day, (-1 * x), [d1])
)
insert #Something
select 3, null union all
select 4, '2016-01-01'
select *
from #Something
I am assuming your expression is correct. just use correct syntax for case.
CREATE TABLE CMAR_GLXXXX(
[FileDate] [date] NULL,
[LoadDTM] [date] NULL,
[AsOfDate] [date] NULL,
[AccountNumber] [varchar](50) NOT NULL,
[CostCenter] [varchar](50) NOT NULL,
[OffSet] [int] default (0) NOT NULL,
[Value] [decimal](10,2) NOT NULL
)
select
(case when [FileDate] IS NULL then NULL else dateadd(day,(-1 * [OffSet]),[FileDate]) end)
from CMAR_GLXXXX

Resources