While loop on SELECT, DELETE ,INSERT statement - sql

I have list of duplicate Transaction IDs and want to insert first the information to table Customer_Deleted_OrderRecordes as log then delete them from Orderstable.
This is my query:
DECLARE #Date Datetime
DECLARE #CustomerName nvarchar(90)
DECLARE #ItemID int
DECLARE #TransactionID int
DECLARE #Invoice int
----selecting values------
SELECT
#Date = O.date,
#CustomerName = c.FullName=,
#ItemID = O.ItemID,
#TransactionID = O.TransactionID,
#Invoice = O.Invoice
FROM
CustomerTable c
INNER JOIN
OrdersTable O ON c.ID = O.CustomerId
WHERE
O.TransactionID IN (1680339,1680340,1680341,1680342,1680343,1680344,1680345,1680346,1680347,1680348,1680349,1680350,1680351,1680352,1680353,1680354,1680355,1680356,1680357,1680358,1680359)
---This will insert values before deleting as log ---
INSERT INTO Customer_Deleted__OrderRecordes ([Date], [CustomerName], [ItemID], [TransactionID], [Invoice])
VALUES (#date, #CustomerName, #ItemID, #TransactionID, #Invoice)
---This will delete values after inserting
DELETE FROM OrdersTable
WHERE TransactionID IN (1680339,1680340,1680341,1680342,1680343,1680344,1680345,1680346,1680347,1680348,1680349,1680350,1680351,1680352,1680353,1680354,1680355,1680356,1680357,1680358,1680359)
My query only effects one row instead of the all values selected in the select statement. How to write a while loop on SELECT, INSERT, DELETE statements?

I don't get why you need to declare variables and use a loop, you can just insert with a select and then delete:
INSERT Customer_Deleted_OrderRecordes
(Date, CustomerName, ItemID, TransactionID, Invoice)
SELECT O.date, c.FullName, O.ItemID, O.TransactionID, O.Invoice
FROM CustomerTable c
INNER JOIN OrdersTable O ON c.ID = O.CustomerId
WHERE O.TransactionID IN
(1680339, 1680340, 1680341, 1680342, 1680343, 1680344, 1680345,
1680346, 1680347, 1680348, 1680349, 1680350, 1680351, 1680352,
1680353, 1680354, 1680355, 1680356, 1680357, 1680358, 1680359)
And then just run the delete.
If you need a loop, (in current situation you wont need one), but as you said in comments there can be a situation where you would need to use variables and insert it according to the values, you can do a while loop like:
while #TransactionID < 1680359
begin
INSERT INTO Customer_Deleted_OrderRecordes (Date, CustomerName, ItemID, TransactionID, Invoice)
VALUES (#date, #CustomerName, #ItemID, #TransactionID, #Invoice)
#TransactionID++
end
You can also add the delete inside the loop before you update the counter variable, so after each inserted row it should delete that row in other table.
But I would highly suggest you not to use a loop when you don't need one, because it would add so much to execution time.

Related

(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.

Insert in to 2 tables from 2 tables from a different database

I am trying to read data from 2 tables and inserting to similar tables in a different database.
Here is my query
BEGIN TRANSACTION
Delete from aspnet_Website.[dbo].[Places]
DECLARE #AutoID int;
insert into aspnet_Website.[dbo].[Places] (ReferenceID, Name, OtherKnownNames, Description, Email)
select ReferenceID, Name, OtherKnownNames, Description, Email from DB_A290D0_places.
[dbo].[places]
where PublishingStatus=0
SELECT #AutoID = scope_identity();
insert into aspnet_Website.[dbo].[Schedules] (Timing, Type, PlaceID)
select [Timing], [Type], #AutoID from DB_A290D0_places.[dbo].[Schedules]
COMMIT
I am getting error
Incorrect syntax near '#AutoID'.
and even I am not sure that it will work or not.
'Places' table has ID field which is auto id and it is used as FK in Schedule table, so for every 'place' row I need to get auto id and insert it into the schedule table along with the corresponding table data from another database.
Update1 : I have fixed the syntax error, I can see records added into the table but last generated AutoID is being used for all rows in child table. I want to use autoid generated for each row.
Update2 : following script worked
BEGIN TRANSACTION
Delete from aspnet_Website.[dbo].[Places]
declare #NewId table (ID int);
insert into aspnet_Website.[dbo].[Places] (ReferenceID, Name, OtherKnownNames, Description, Email)
select ReferenceID, Name, OtherKnownNames, Description, Email from DB_A290D0_places.
[dbo].[places]
where PublishingStatus=0
output Inserted.ID into #NewId (ID)
insert into aspnet_Website.[dbo].[Schedules] (Timing, [Type], PlaceID)
select [Timing], [Type], P.ID
from DB_A290D0_places.[dbo].[Schedules] S
inner join #NewId P on P.ID = S.PlaceId;
COMMIT
You can't store more than one value in a variable, the the line SELECT #AutoID = scope_identity(); will only capture the last id inserted.
To solve you problem have you considered not changing the ids by setting IDENTITY_INSERT ON and inserting the original ids?
Otherwise use the OUTPUT clause to capture the new ids, map them to the old ids, and then insert them into the Schedules table.
begin transaction
delete from aspnet_Website.[dbo].[Places];
declare #AutoID int;
declare #NewId table (ID int, OldID int);
-- In an insert statement you can't access the source table in the output clause unfortunately
/*
insert into aspnet_Website.[dbo].[Places] (ReferenceID, [Name], OtherKnownNames, [Description], Email)
output Inserted.ID, P.ID into #NewId (ID, OldID)
select ReferenceID, [Name], OtherKnownNames, [Description], Email
from DB_A290D0_places.[dbo].[places] P
where PublishingStatus = 0;
*/
-- However in a merge statement you can access both the source and destination tables in the output clause.
merge into aspnet_Website.[dbo].[Places] T
using DB_A290D0_places.[dbo].[places] S on 1 = 0 -- always false
when not matched by target and S.PublishingStatus = 0 then -- happens for every row, because 1 is never 0
insert (ReferenceID, [Name], OtherKnownNames, [Description], Email)
values (S.ReferenceID, S.[Name], S.OtherKnownNames, S.[Description], S.Email)
output Inserted.ID, S.ID into into #NewId (ID, OldID);
insert into aspnet_Website.[dbo].[Schedules] (Timing, [Type], PlaceID)
select [Timing], [Type], P.ID
from DB_A290D0_places.[dbo].[Schedules] S
inner join #NewId P on P.OldId = S.PlaceId;
commit

SQL Server : scalar function returns nothing, but it should return INT

This is my function:
CREATE FUNCTION fu_order_customer_adress (#customer_ID INT)
RETURNS INT AS
BEGIN
RETURN (
SELECT c.adress_ID FROM customers AS c
JOIN orders AS o ON (c.customer_ID = o.customer_ID)
WHERE c.customer_ID = #customer_ID)
END
It is used in AFTER INSERT trigger of table orders, which has following structure (without unimportant columns):
orders (order_ID INT, customer_ID INT, employee_ID)
order_ID is the primary key, the other columns are foreign keys.
Trigger looks like this:
CREATE TRIGGER tr_orders_insert ON orders
AFTER INSERT
AS
BEGIN
DECLARE #order_ID INT = (SELECT order_ID FROM INSERTED);
DECLARE #customer_ID INT = (SELECT customer_ID FROM INSERTED);
UPDATE orders
SET adress_ID = (SELECT dbo.fu_order_customer_adress(#customer_ID))
WHERE order_ID = #order_ID;
END
Following inserts are numbered to be later easily pointed to.
(1) This works fine for inserts with different customer_ID:
INSERT INTO orders(customer_ID, employee_ID)
VALUES (1, 1)
INSERT INTO orders(customer_ID, employee_ID)
VALUES (2, 2)
(2) But when order with already used customer_ID is inserted, insert will end with error:
INSERT INTO orders(customer_ID, employee_ID)
VALUES (1, 2)
I figured out that it's caused by that function used in trigger, because in this case it returns nothing.
I tried to put SELECT function and SELECT used in function into trigger:
CREATE TRIGGER tr_orders_insert ON objednavka
AFTER INSERT
AS
BEGIN
DECLARE #order_ID INT = (SELECT order_ID FROM INSERTED);
DECLARE #customer_ID INT = (SELECT customer_ID FROM INSERTED);
SELECT c.adress_ID
FROM customers AS c
JOIN orders AS o ON (c.customer_ID = o.customer_ID)
WHERE c.customer_ID = #customer_ID)
SELECT dbo.fu_order_customer_adress(#customer_ID)
UPDATE orders
SET adress_ID = (SELECT dbo.fu_order_customer_adress(#customer_ID))
WHERE order_ID = #order_ID;
END
In (1) are both SELECT results same.
In (2) is SELECT result ok, but SELECT function returns nothing.
I don't understand what's wrong...thanks for help!
For your this simple trigger I dont think you need this Performance killer Scalar function you can simply do the following without using any Scalar functions and simply joining your tables with Inserted table .
Also you have major issues with your logic in your trigger, it will only work for a single insert and will fail if there are multiple Inserts in your Orders table. A much safer and performance efficient approach would be something like .......
CREATE TRIGGER tr_orders_insert ON orders
AFTER INSERT AS
BEGIN
SET NOCOUNT ON;
UPDATE O
SET O.adress_ID = C.adress_ID
FROM orders O
INNER JOIN inserted AS I ON O.order_ID = I.order_ID
INNER JOIN customers AS c ON C.customer_ID = I.customer_ID
END

Insert Into two tables with conditions

Im doing a reservation application and was wondering how would i handle the following scenario; if a booking has 2 or more "extra" items on it how do i create a SP or a trigger even to handle this? the SP i have now works fine for a booking with a single extra item on it. Im i making any sense?
ALTER PROCEDURE [dbo].[CreateBooking]
#DateFrom datetime,
#DateTo datetime,
#RoomID int,
#PersonID int,
#ProductID int,
#OrderAmount int
AS
BEGIN TRANSACTION
SET NOCOUNT ON;
INSERT INTO booking(created_on, startdate, enddate, room_id, person_id)
VALUES (getdate(), #DateFrom, #DateTo, #RoomID, #PersonID)
IF ##error <> 0
ROLLBACK TRANSACTION
ELSE
INSERT INTO booking_details (booking_id,prod_id,order_amount)
VALUES (SCOPE_IDENTITY(), #ProductID, #OrderAmount)
COMMIT TRANSACTION
You can pass an xml parameter then loop through it and write them.
CREATE PROCEDURE
SelectByIdList(#productIds xml) AS
DECLARE #Products TABLE (ID int)
INSERT INTO #Products (ID) SELECT
ParamValues.ID.value('.','VARCHAR(20)')
FROM #productIds.nodes('/Products/id')
as ParamValues(ID)
SELECT * FROM
Products INNER JOIN
#Products p ON Products.ProductID = p.ID

Selecting specific rows from DELETED table within a trigger

I have an intermediate table that defines many-to-many relationship between, for instance, Customer and Orders, like this:
USE [master]
GO
CREATE DATABASE Example
GO
USE [Example]
GO
CREATE TABLE [dbo].[CustomerOrders](
[CustomerId] [int],
[OrderId] [int]
)
GO
INSERT INTO CustomerOrders (CustomerId, OrderId) VALUES (1, 1)
INSERT INTO CustomerOrders (CustomerId, OrderId) VALUES (1, 2)
INSERT INTO CustomerOrders (CustomerId, OrderId) VALUES (2, 1)
GO
CREATE TRIGGER [dbo].[CustomerOrdersRemoved]
ON [dbo].[CustomerOrders]
FOR DELETE
AS
BEGIN
SET NOCOUNT ON;
--IF NOT EXISTS (SELECT CustomerId FROM CustomerOrders
INNER JOIN deleted ON CustomerOrders.CustomerId=deleted.CustomerId)
--this wont work
END
GO
DELETE CustomerOrders WHERE OrderId=1
GO
Now, I need to have an ON DELETE trigger on this table, which would need to update another table based on customers who don't have any orders left in the table. In this case, after the DELETE operation the customer with CustomerId=1 will have 1 order with OrderId=2 left and customer with CustomerId=2 will have no orders left. So I need to get only customer with CustomerId=2 from the deleted vtable within the trigger.
How can I accomplish this?
The problem is that you're using deleted.CustomerId on the right side of a comparison.
I was doing something similar to what you're doing.
and the solution is to define a #variable to hold deleted.CustomerId (After your SET NO COUNT line), then compare CustomerOrders.CustomerId to the variable.
EDIT:
Here's the code which works just fine, It is a trigger to update movie rating by the average rating it got from tblRating(MovieID, UserID, rating) which also represents a many-to-many relationship between tblMovies and tblUsers, this one uses inserted but it is the same with deleted:
CREATE TRIGGER trUpdateRating
ON [dbo].[tblRating]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
DECLARE #rate float, #mid int
SET #rate = 0;
-- here is the assignment I was talking about and it is valid
SELECT #mid=MovieID from inserted;
SELECT #rate=AVG(isnull(Rating, 0)) FROM tblRating WHERE MovieID=#mid;
-- and here is the comparison
UPDATE tblMovies
SET avg_rating = #rate
WHERE ID=#mid;
END
My hands are faster than my head :-/
The answer is
SELECT CustomerId FROM deleted WHERE CustomerId NOT IN (SELECT CustomerId FROM CustomerOrders)