i want copy data from table to another without duplicate using triggers - sql

i have two tables
in table number one it's add rows every moment depend on user insert
and in table number two i want to copy every row has been added on table one but with specific criteria
and also table number two have some additional columns i need them later
i want do that by triggers but i don't know how to do that
i'v already try stored procedure but not automatically
INSERT INTO ax.RETAILTRANSACTIONSALESTRANS2
(
[BARCODE]
, [CREATEDDATETIME]
,[Name]
, [ITEMID]
, [NETAMOUNTINCLTAX]
, [QTY]
, [STAFFID]
, [TERMINALID]
, [TRANSDATE]
,[PERIODICPERCENTAGEDISCOUNT]
,[ReceiptID]
)
SELECT ab.[BARCODE]
, ab.[CREATEDDATETIME]
,[ax].[ECORESPRODUCTTRANSLATION].[NAME]
, ab.[ITEMID]
, ab.[PRICE]
, ab.[QTY]
, ab.[STAFFID]
, ab.[TERMINALID]
, ab.[TRANSDATE]
, ab.[PERIODICPERCENTAGEDISCOUNT]
,ab.RECEIPTID
FROM ax.RetailTransactionSalesTrans ab
inner join [ax].[INVENTTABLE] on ab.[ITEMID] =[ax].[INVENTTABLE].[ITEMID]
inner join [ax].[ECORESPRODUCTTRANSLATION] on [ax].[INVENTTABLE].PRODUCT =[ax].[ECORESPRODUCTTRANSLATION].[PRODUCT]
LEFT JOIN ax.RETAILTRANSACTIONSALESTRANS2 a ON
a.[BARCODE] = ab.[BARCODE]
AND a.[CREATEDDATETIME] = ab.[CREATEDDATETIME]
AND a.[ITEMID] = ab.[ITEMID]
AND a.[NETAMOUNTINCLTAX] = ab.[PRICE]
AND a.[QTY] = ab.[QTY]
AND a.[STAFFID] = ab.[STAFFID]
AND a.[TERMINALID] = ab.[TERMINALID]
AND a.[TRANSDATE] = ab.[TRANSDATE]
AND a.[PERIODICPERCENTAGEDISCOUNT]=ab.[PERIODICPERCENTAGEDISCOUNT]
and a.Process=null and a.Checked=null and a.[Select]=null
where ab.[QTY]>0 and ab.[RECEIPTID]!='' and ab.[TRANSDATE] >= DATEADD(day, #daycount*-1, GetDate())
and NOT EXISTS(select * from ax.RETAILTRANSACTIONSALESTRANS2 where [CREATEDDATETIME] = ab.[CREATEDDATETIME])
i want to convert that as trigger but i don't how do that
note: #daycount i take it from another table but i pass that by my program

you can use the below and edit the needed columns , the idea is FROM Inserted you can select all rows that inserted to the main table.
CREATE TRIGGER TRetailTransactionSalesTrans ON RetailTransactionSalesTrans
FOR INSERT
AS
BEGIN
INSERT INTO RETAILTRANSACTIONSALESTRANS2
SELECT [BARCODE]
,[CREATEDDATETIME]
,[Name]
,[ITEMID]
,[NETAMOUNTINCLTAX]
,[QTY]
,[STAFFID]
,[TERMINALID]
,[TRANSDATE]
,[PERIODICPERCENTAGEDISCOUNT]
,[ReceiptID]
FROM Inserted;
END

Related

SCD Type 2 - Handling Intraday changes?

I have a merge statement that builds my SCD type 2 table each night. This table must house all historical changes made in the source system and create a new row with the date from/date to columns populated along with the "islatest" flag. I have come across an issue today that I am not really sure how to handle.
There looks to have been multiple changes to the source table within a 24 hour period.
ID Code PAN EnterDate Cost Created
16155 1012401593331 ENRD 2015-11-05 7706.3 2021-08-17 14:34
16155 1012401593331 ENRD 2015-11-05 8584.4 2021-08-17 16:33
I use a basic merge statement to identify my changes however what would be the best approach to ensure all changes get picked up correctly? The above is giving me an error as it's trying to insert/update multiple rows with the same value
DECLARE #DateNow DATETIME = Getdate()
IF Object_id('tempdb..#meteridinsert') IS NOT NULL
DROP TABLE #meteridinsert;
CREATE TABLE #meteridinsert
(
meterid INT,
change VARCHAR(10)
);
MERGE
INTO [DIM].[Meters] AS target
using stg_meters AS source
ON target.[ID] = source.[ID]
AND target.latest=1
WHEN matched THEN
UPDATE
SET target.islatest = 0,
target.todate = #Datenow
WHEN NOT matched BY target THEN
INSERT
(
id,
code,
pan,
enterdate,
cost,
created,
[FromDate] ,
[ToDate] ,
[IsLatest]
)
VALUES
(
source.id,
source.code ,
source.pan ,
source.enterdate ,
source.cost ,
source.created ,
#Datenow ,
NULL ,
1
)
output source.id,
$action
INTO #meteridinsert;INSERT INTO [DIM].[Meters]
(
[id] ,
[code] ,
[pan] ,
[enterdate] ,
[cost] ,
[created] ,
[FromDate] ,
[ToDate] ,
[IsLatest]
)
SELECT ([id] ,[code] ,[pan] ,[enterdate] ,[cost] ,[created] , #DateNow ,NULL ,1 FROM stg_meters a
INNER JOIN #meteridinsert cid
ON a.id = cid.meterid
AND cid.change = 'UPDATE'
Maybe you can do it using merge statement, but I would prefer to use typicall update and insert approach in order to make it easier to understand (also I am not sure that merge allows you to use the same source record for update and insert...)
First of all I create the table dimscd2 to represent your dimension table
create table dimscd2
(naturalkey int, descr varchar(100), startdate datetime, enddate datetime)
And then I insert some records...
insert into dimscd2 values
(1,'A','2019-01-12 00:00:00.000', '2020-01-01 00:00:00.000'),
(1,'B','2020-01-01 00:00:00.000', NULL)
As you can see, the "current" is the one with descr='B' because it has an enddate NULL (I do recommend you to use surrogate keys for each record... This is just an incremental key for each record of your dimension, and the fact table must be linked with this surrogate key in order to reflect the status of the fact in the moment when happened).
Then, I have created some dummy data to represent the source data with the changes for the same natural key
-- new data (src_data)
select 1 as naturalkey,'C' as descr, cast('2020-01-02 00:00:00.000' as datetime) as dt into src_data
union all
select 1 as naturalkey,'D' as descr, cast('2020-01-03 00:00:00.000' as datetime) as dt
After that, I have created a temp table (##tmp) with this query to set the enddate for each record:
-- tmp table
select naturalkey, descr, dt,
lead(dt,1,0) over (partition by naturalkey order by dt) enddate,
row_number() over (partition by naturalkey order by dt) rn
into ##tmp
from src_data
The LEAD function takes the next start date for the same natural key, ordered by date (dt).
The ROW_NUMBER marks with 1 the oldest record in the source data for the natural key in the dimension.
Then, I proceed to close the "current" record using update
update d
set enddate = t.dt
from dimscd2 d
join ##tmp t
on d.naturalkey = t.naturalkey
and d.enddate is null
and t.rn = 1
And finally I add the new source data to the dimension with insert
insert into dimscd2
select naturalkey, descr, dt,
case enddate when '1900-00-00' then null else enddate end
from ##tmp
Final result is obtained with the query:
select * from dimscd2
You can test on this db<>fiddle

(SQL Merge) I am getting duplicates in the table

We have a daily stream where we are getting the list of customers using various products.
I am trying to create a table for the customers where we can track their changes, and at the same time, we can get a distinct list of customers.
The stream contains thousands of records each day. That was the reason we thought we should move from SCD Type 1 to SCD Type 2.
We want to implement this procedure so that it will run each day and get the records from the last day and compare them to the whole table. If the customer has any change, it will mark that row as 0 and get the new row and mark it as 1.
But in this process, I am getting the new records, but I am also getting duplicate data when I am running the stored procedure.
Please guide.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Create PROC [dbo].[sp_UpdateCustomerInfoHistory] AS BEGIN
SET
NOCOUNT ON --Truncate Table [dbo].[CustomerInfoHistory];
DECLARE #TODAY DATE = GETDATE();
DECLARE #YESTERDAY DATE = GETDATE() - 1;
WITH CTE AS (
SELECT
DISTINCT(a.CustomerId) AS CustomerId,
ISNULL(b.[CustomerName], a.[CustomerName]) AS CustomerName,
ISNULL(b.[CurrentDefaultDomain], a.[CustomerName]) AS CurrentDefaultDomain,
ISNULL(b.[CustomerCountryCode], 'Unknown') AS CustomerCountryCode,
ISNULL(b.[HasC], 0) AS HasC,
ISNULL(b.[HasG], 0) AS HasG,
ISNULL(b.[IsV], 0) AS IsV,
ISNULL(
ISNULL(b.[CustomerCreatedDate], a.[ProductCreatedTimeUtc]),
#TODAY
) AS CustomerCreatedDate,
ISNULL(b.[CustomerState], 'Active') AS CustomerState,
ISNULL(b.[CustomerType], 'RegularCustomer') AS CustomerType,
ISNULL(b.[DataCenterProduct], 'Unknown') AS DataCenterProduct,
ISNULL(b.[DataCenterModel], 'Unknown') AS DataCenterModel,
ISNULL(b.[IsTestCustomer], 0) AS IsTestCustomer,
ISNULL(b.[CommunicationLanguage], 'Unknown') AS CommunicationLanguage,
ISNULL(b.[IsInternal], 0) AS IsInternal,
ISNULL(b.[IndustryName], 'N/A') AS IndustryName,
ISNULL(c.MappingID, 0) AS MappingID
FROM
[dbo].[ProductDetails] AS a
LEFT JOIN [Common].[vwdimCustomer_Staging] AS b ON a.CustomerId = b.CustomerId
LEFT JOIN [Common].[vwmapCustomerMappingID_Staging] AS c ON b.CustomerId = c.CustomerId
WHERE a.[TIMESTAMP] = #YESTERDAY
), CTE1 AS (
Select *, BINARY_CHECKSUM(
CustomerId,
CustomerName,
IsTestCustomer,
IsInternal
) AS MKEY
from CTE)
MERGE INTO [dbo].[CustomerInfoHistory] AS T USING CTE1 AS S ON T.[MKEY] = S.[MKEY]
WHEN MATCHED
AND [Current_Flag] = 1
AND T.CustomerName <> S.CustomerName THEN
UPDATE
SET
T.Current_Flag = 0,
T.End_date = #YESTERDAY
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
CustomerId,
CustomerName,
CurrentDefaultDomain,
CustomerCountryCode,
HasC,
HasG,
IsV,
CustomerCreatedDate,
CustomerState,
CustomerType,
DataCenterProduct,
DataCenterModel,
IsTestCustomer,
CommunicationLanguage,
IsInternal,
IndustryName,
MappingID,
Eff_Date,
End_Date,
Current_Flag,
MKEY,
RefreshedDate
)
VALUES
(
S.CustomerId,
S.CustomerName,
S.CurrentDefaultDomain,
S.CustomerCountryCode,
S.HasC,
S.HasG,
S.IsV,
S.CustomerCreatedDate,
S.CustomerState,
S.CustomerType,
S.DataCenterProduct,
S.DataCenterModel,
S.IsTestCustomer,
S.CommunicationLanguage,
S.IsInternal,
S.IndustryName,
S.MappingID,
#YESTERDAY,
'12/31/2099',
1,
S.MKEY,
#TODAY
);
END
I think you can use MERGE in Azure Synapse. It will insert new rows or update old rows based on the primary key value.
For example:
Create table:
CREATE TABLE dbo.CustomerInfoHistory (
CustomerId int NOT NULL,
CustomerName nvarchar(254) NOT NULL,
CurrentDefaultDomain nvarchar(max) NULL
);
GO
ALTER TABLE dbo.CustomerInfoHistory ADD CONSTRAINT PK__kruserpr__6E092EE804688C07 PRIMARY KEY (CustomerId, CustomerName);
GO
Create a Table-valued parameter named dbo.CustomerInfoHistory_type, it will be used in my stored procedure:
create TYPE dbo.CustomerInfoHistory_type AS TABLE(
CustomerId int NOT NULL,
CustomerName nvarchar(254) NOT NULL,
CurrentDefaultDomain nvarchar(max)
)
GO
Create a Stored procedure, it will merge the same records and insert new records based on the primary key:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create PROCEDURE [dbo].[spUpsertCustomerInfoHistory]
#profile dbo.CustomerInfoHistory_type READONLY
AS
BEGIN
MERGE dbo.CustomerInfoHistory AS target_sqldb
USING #profile AS source_tblstg
ON (target_sqldb.CustomerId = source_tblstg.CustomerId and target_sqldb.CustomerName = source_tblstg.CustomerName )
WHEN MATCHED THEN
UPDATE SET
CurrentDefaultDomain = source_tblstg.CurrentDefaultDomain
WHEN NOT MATCHED THEN
INSERT (
CustomerId,
CustomerName,
CurrentDefaultDomain
)
VALUES (
source_tblstg.CustomerId,
source_tblstg.CustomerName,
source_tblstg.CurrentDefaultDomain
);
END
GO
After that, we can execute the stored procedure by following code:
DECLARE #profileVar AS dbo.CustomerInfoHistory_type;
/* Add data to the table variable. */
INSERT INTO #profileVar (CustomerId, CustomerName, CurrentDefaultDomain) values (1, 'tom','wednesday');
exec [dbo].[spUpsertCustomerInfoHistory] #profileVar
That's all.

How to update the source table in the MERGE statement?

I have a table tbBroker on my local database and a table Agency on linked server object "DIS".
I am trying to migrate data from local's tbBroker to DIS's Agency table
Also note that
tbBroker's Columns = Agency's Columns
BrokerCode = AgencyNumber
BusinesssName = AgencyName
City = City
tbSystemUser.EmailAddress = EmailAddress
Also, tbBroker has two additional columns DISImportFlag bit and DISCreatTS datetime. These two fields should be updated to 1 and GETDATE() whenever data is migrated (during insertion) from tbBroker to Agency table in order to determine which rows have been migrated.
I have written the following query
USE [DISTemp];
MERGE INTO
[dbo].[Agency] AS [TARGET]
USING
[aginbr].[dbo].[tbBroker] AS [SOURCE]
ON
[TARGET].[AgencyNumber] COLLATE Latin1_General_CI_AI = [SOURCE].[BrokerCode]
WHEN NOT MATCHED BY TARGET THEN
INSERT (
[AgencyName]
, [Address1]
, [Address2]
, [PostalCode]
, [City]
, [Phone]
, [EmailAddress]
)
VALUES (
[SOURCE].[BUSINESSNAME]
, [SOURCE].[ADDRESS]
, [SOURCE].[AddressLine2]
, [SOURCE].[Zip]
, [SOURCE].[City]
, [SOURCE].[Phone]
, [SOURCE].[Email]
)
UPDATE SET [SOURCE].[DISImportFlag] = 1,[SOURCE].[DISCreatTS] = GETDATE()
WHEN MATCHED THEN
UPDATE SET
[TARGET].[AgencyName] = [SOURCE].[BUSINESSNAME]
, [TARGET].[Address1] = [SOURCE].[ADDRESS]
, [TARGET].[Address2] = [SOURCE].[AddressLine2]
, [TARGET].[PostalCode] = [SOURCE].[Zip]
, [TARGET].[City] = [SOURCE].[City]
, [TARGET].[Phone] = [SOURCE].[Phone]
, [TARGET].[EmailAddress] = [SOURCE].[Email];
When we execute the above query it throws error message
A MERGE statement must be terminated by a semi-colon (;)
after adding the following line
UPDATE SET [SOURCE].[DISImportFlag] = 1,[SOURCE].[DISCreatTS] = GETDATE()
Edit from comments
The source table tbBroker has a primary key column BrokerID.
I want to update in the source table those rows that were inserted into the target table, i.e. those rows that didn't exist in the target table before MERGE.
I don't think you can change some table other than TARGET using a single MERGE statement. But, you can use OUTPUT clause to capture the result of the MERGE in a helper/temporary table and then update your SOURCE based on that.
You said that you want to update only those rows that were inserted into the Target, i.e. those that didn't exist in the Target before MERGE.
Output IDs of inserted rows into the temporary table and then use it to update the Source table.
CREATE TABLE #Temp(
BrokerID int NOT NULL,
CONSTRAINT [PK_Broker] PRIMARY KEY CLUSTERED
(
BrokerID ASC
));
INSERT INTO #Temp (BrokerID)
SELECT TableChanges.BrokerID
FROM
(
MERGE INTO [dbo].[Agency] AS [TARGET]
USING [aginbr].[dbo].[tbBroker] AS [SOURCE]
ON [TARGET].[AgencyNumber] COLLATE Latin1_General_CI_AI = [SOURCE].[BrokerCode]
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
[AgencyName]
, [Address1]
, [Address2]
, [PostalCode]
, [City]
, [Phone]
, [EmailAddress]
)
VALUES
(
[SOURCE].[BUSINESSNAME]
, [SOURCE].[ADDRESS]
, [SOURCE].[AddressLine2]
, [SOURCE].[Zip]
, [SOURCE].[City]
, [SOURCE].[Phone]
, [SOURCE].[Email]
)
WHEN MATCHED THEN
UPDATE SET
[TARGET].[AgencyName] = [SOURCE].[BUSINESSNAME]
, [TARGET].[Address1] = [SOURCE].[ADDRESS]
, [TARGET].[Address2] = [SOURCE].[AddressLine2]
, [TARGET].[PostalCode] = [SOURCE].[Zip]
, [TARGET].[City] = [SOURCE].[City]
, [TARGET].[Phone] = [SOURCE].[Phone]
, [TARGET].[EmailAddress] = [SOURCE].[Email]
OUTPUT $action, [SOURCE].BrokerID
) AS TableChanges (MergeAction, BrokerID)
WHERE TableChanges.MergeAction = 'INSERT'
;
UPDATE [aginbr].[dbo].[tbBroker]
SET
[DISImportFlag] = 1
,[DISCreatTS] = GETDATE()
WHERE
BrokerID IN
(
SELECT T.BrokerID FROM #Temp AS T
)
;
DROP TABLE #Temp;
Obviously, wrap this whole thing in a transaction and TRY ... CATCH and add appropriate error handling.

Query Slow down due to structure of WHERE clause

I have a query that creates an #TABLE of a population of interest. It's structure is like this:
DECLARE #SepsisTbl TABLE (
PK INT IDENTITY(1, 1) PRIMARY KEY
, Name VARCHAR(500)
, MRN INT
, Account INT
, Age INT -- Age at arrival
, Arrival DATETIME
, Triage_StartDT DATETIME
, Left_ED_DT DATETIME
, Disposition VARCHAR(500)
, Mortality CHAR(1)
);
WITH Patients AS (
SELECT UPPER(Patient) AS [Name]
, MR#
, Account
, DATEDIFF(YEAR, AgeDob, Arrival) AS [Age_at_Arrival]
, Arrival
, Triage_Start
, TimeLeftED
, Disposition
, CASE
WHEN Disposition IN (
'Medical Examiner', 'Morgue'
)
THEN 'Y'
ELSE 'N'
END AS [Mortality]
FROM SMSDSS.c_Wellsoft_Rpt_tbl
WHERE Triage_Start IS NOT NULL
AND (
Diagnosis LIKE '%SEPSIS%'
OR
Diagnosis LIKE '%SEPTIC%'
)
)
INSERT INTO #SepsisTbl
SELECT * FROM Patients
From this point forward I have 5 more queries of the same sort that are looking for different types of orders that I then LEFT OUTER JOIN onto this table. My question is, why does my performance degrade so much when I change the where clause of the tables from this:
AND A.Account IN (
SELECT Account
FROM SMSDSS.c_Wellsoft_Rpt_tbl
WHERE (
Diagnosis LIKE '%SEPSIS%'
OR
Diagnosis LIKE '%SEPTIC%'
)
to this:
AND A.Account IN (
SELECT Account
FROM #SepsisTbl
)
The run time goes from 2.5 minutes to over 10 minutes with still no results. The CTE itself runs as fast as I can press F5.
Thank you,
I suspect that the problem is because the table variable doesn't have an index on Account. If you add an index on Account then I would expect better performance.
See the answer to this question for details on how to add an index: Creating an index on a table variable

SQL Server 2008 - Stored inserted IDs in a variable table for another query use later

I have an INSERT query to insert multiple records and returns a set if CompanyID that inserted, and this query is working fine. I would like to stored those returned CompanyIDs into a table variable, so I can run another query after that using those CompanyIDs. I tried, but got stuck.
Please help.
-- Create a table variable
DECLARE #CompanyIDs TABLE
(
CompanyID INT NOT NULL
)
INSERT INTO #CompanyIDs -- STUCK right here and it failed
-- This INSERT query is working fine, and will return the CompanyIDs that inserted.
INSERT INTO [Apps].[dbo].[ERP_Company]
(
[Name]
,[Type]
,[ImportFrom]
,[InActiveFlag]
,[CreatedDate]
,[CreatedBy]
,[CompanyOwnerID]
,[ModifiedDate]
,[HQAddress]
,[HQCity]
,[HQState]
,[HQZip]
,[Type2]
,[Segment]
,[Industry]
)
output inserted.CompanyID
SELECT DISTINCT(Company) AS Company
, 'End-User'
, 'MarketingUpload'
, '0'
, GETDATE()
, '1581'
, '1581'
, GETDATE()
, [Address]
, City
, [State]
, ZipCode
, 'Customer'
, Segment
, Industry
FROM dbo.Import_CompanyContact icc
WHERE RefNum = 14
AND MatchFlag = 3
AND NOT Exists (SELECT * FROM dbo.ERP_Company
WHERE REPLACE(Name, '''', '') = REPLACE(icc.Company, '''', '')
)
OUTPUT inserted.CompanyID INTO #YourTable