Related
The Problem
I'm trying to detect and react to changes in a table where each update is being recorded as a new row with some values being the same as the original, some changed (the ones I want to detect) and some NULL values (not considered changed).
For example, given the following table MyData, and assuming the OrderNumber is the common value,
ID OrderNumber CustomerName PartNumber Qty Price OrderDate
1 123 Acme Corp. WG301 4 15.02 2020-01-02
2 456 Base Inc. AL337 7 20.15 2020-02-03
3 123 NULL WG301b 5 19.57 2020-01-02
If I execute the query for OrderNumber = 123 I would like the following data returned:
Column OldValue NewValue
ID 1 3
PartNumber WG301 WG301b
Qty 4 5
Price 15.02 19.57
Or possibly a single row result with only the changes filled, like this (however, I would strongly prefer the former format):
ID OrderNumber CustomerName PartNumber Qty Price OrderDate
3 NULL NULL WG301b 5 19.57 NULL
My Solution
I have not had a chance to test this, but I was considering writing the query with the following approach (pseudo-code):
select
NewOrNull(last.ID, prev.ID) as ID,
NewOrNull(last.OrderNumber, prev.OrderNumber) as OrderNumber
NewOrNull(last.CustomerName, prev.CustomerName) as CustomerName,
...
from last row with OrderNumber = 123
join previous row where OrderNumber = 123
Where the function NewOrNull(lastVal, prevVal) returns NULL if the values are equal or lastVal value is NULL, otherwise the lastVal.
Why I'm Looking for an Answer
I'm afraid that the ugly join, the number of calls to the function, and the procedural approach may make this approach not scalable. Before I start down the rabbit hole, I was wondering...
The Question
...are there any other approaches I should try, or any best practices to solving this specific type of problem?
I came up with a solution for the second (less preferred) format:
The Data
Using the following data:
INSERT INTO MyData
([ID], [OrderNumber], [CustomerName], [PartNumber], [Qty], [Price], [OrderDate])
VALUES
(1, 123, 'Acme Corp.', 'WG301', '4', '15.02', '2020-01-02'),
(2, 456, 'Base Inc.', 'AL337', '7', '20.15', '2020-02-03'),
(3, 123, NULL, 'WG301b', '5', '19.57', '2020-01-02'),
(4, 123, 'ACME Corp.', 'WG301b', NULL, NULL, '2020-01-02'),
(6, 456, 'Base Inc.', NULL, '7', '20.15', '2020-02-05');
The Function
This function returns the updated value if it has changed, otherwise NULL:
CREATE FUNCTION dbo.NewOrNull
(
#newValue sql_variant,
#oldValue sql_variant
)
RETURNS sql_variant
AS
BEGIN
DECLARE #ret sql_variant
SELECT #ret = CASE
WHEN #newValue IS NULL THEN NULL
WHEN #oldValue IS NULL THEN #newValue
WHEN #newValue = #oldValue THEN NULL
ELSE #newValue
END
RETURN #ret
END;
The Query
This query returns the history of changes for the given order number:
select dbo.NewOrNull(new.ID, old.ID) as ID,
dbo.NewOrNull(new.OrderNumber, old.OrderNumber) as OrderNumber,
dbo.NewOrNull(new.CustomerName, old.CustomerName) as CustomerName,
dbo.NewOrNull(new.PartNumber, old.PartNumber) as PartNumber,
dbo.NewOrNull(new.Qty, old.Qty) as Qty,
dbo.NewOrNull(new.Price, old.Price) as Price,
dbo.NewOrNull(new.OrderDate, old.OrderDate) as OrderDate
from MyData new
left join MyData old
on old.ID = (
select top 1 ID
from MyData pre
where pre.OrderNumber = new.OrderNumber
and pre.ID < new.ID
order by pre.ID desc
)
where new.OrderNumber = 123
The Result
ID OrderNumber CustomerName PartNumber Qty Price OrderDate
1 123 Acme Corp. WG301 4 15.02 2020-01-02
3 (null) (null) WG301b 5 19.57 (null)
4 (null) ACME Corp. (null) (null) (null) (null)
The Fiddle
Here's the SQL Fiddle that shows the whole thing in action.
http://sqlfiddle.com/#!18/b720f/5/0
I have table that has Order_Day,Product_ID, Quantity and Price columns:
create table dbo.AmazonTestTable
(order_day date,
order_id int,
product_id char(2),
quantity int,
price int)
go
insert into dbo.AmazonTestTable values
('01-JUL-11',1,'p1',5,5),
('01-JUL-11',2,'p2',2,10),
('01-JUL-11',3,'p3',10,25),
('01-JUL-11',4,'p1',20,5),
('02-JUL-11',5,'p3',5,25),
('02-JUL-11',6,'p4',6,20),
('02-JUL-11',7,'p1',2,5),
('02-JUL-11',8,'p5',1,50),
('02-JUL-11',9,'p6',2,50),
('02-JUL-11',10,'p2',4,10)
go
The problem is that I want to select order_day, product_id, and TotalSales(this can be calculated by using quantity multiple by price) and then pivot it. So the pivoted table should have three columns: product_id, '01-JUL-11','02-JUL-11'. So I use the codes below:
select product_id,'2011-07-01' as TotalSalesOnDay1,'2011-07-02' as
TotalSalesOnDay2
from (select product_id,order_day,(quantity*price) as TotalSales
from dbo.AmazonTestTable) as TotalSalesPerDay
pivot
(sum(TotalSales) for [order_day] in ([2011-07-01] ,[2011-07-02] )) as
PivotDataSet;
go
but it returns the result as below:
'2011-07-01' is a constant string. The escape method in SQL Server uses square braces:
select product_id, [2011-07-01] as TotalSalesOnDay1, [2011-07-02] as TotalSalesOnDay2
. . .
This query will do the trick -
;With PivotData
As
(
Select
product_id, -- grouping element
order_day, -- spreading element
(quantity * price) As TotalSales -- aggregating element
From AmazonTestTable
)
Select
product_id, [2011-07-01] As TotalSalesDay1, [2011-07-02] As TotalSalesDay2
From PivotData
Pivot (Sum(TotalSales) For order_day In ([2011-07-01], [2011-07-02])) As P
It will give you the following output -
product_id TotalSalesDay1 TotalSalesDay2
p1 125 10
p2 20 40
p4 NULL 120
p3 250 125
p5 NULL 50
p6 NULL 100
Do you want something like this
SELECT product_id, order_day, SUM(TotalSales) AS TotalSales FROM
(SELECT product_id,order_day,(quantity*price) as TotalSales
FROM AmazonTestTable) AS TotalSalesPerDay
GROUP BY TotalSalesPerDay.order_day;
The output will be:
product_id| order_day | TotalSales
p1 | 01-JUL-11 |395
p2 | 02-JUL-11 |445
I have seen hundreds of pivot questions here in the past few months. Let me post some sample solutions, and maybe help others to learn the basics, so we don't keep seeing the same thing over and over and over.
CREATE TABLE Products (
product VARCHAR(30),
market_year INT,
value INT,
quantity INT,
CONSTRAINT pk_products
PRIMARY KEY (product, market_year));
INSERT INTO Products VALUES('Corn', 2003, 100, 20);
INSERT INTO Products VALUES('Corn', 2004, 200, 25);
INSERT INTO Products VALUES('Corn', 2005, 150, 30);
INSERT INTO Products VALUES('Corn', 2006, 150, 10);
SELECT product,
SUM(CASE WHEN market_year = 2003 THEN value ELSE 0 END) AS v2003,
SUM(CASE WHEN market_year = 2003 THEN quantity ELSE 0 END) AS q2003,
SUM(CASE WHEN market_year = 2004 THEN value ELSE 0 END) AS v2004,
SUM(CASE WHEN market_year = 2004 THEN quantity ELSE 0 END) AS q2004,
SUM(CASE WHEN market_year = 2005 THEN value ELSE 0 END) AS v2005,
SUM(CASE WHEN market_year = 2005 THEN quantity ELSE 0 END) AS q2005,
SUM(CASE WHEN market_year = 2006 THEN value ELSE 0 END) AS v2006,
SUM(CASE WHEN market_year = 2006 THEN quantity ELSE 0 END) AS q2006
FROM Products
GROUP BY product;
Result:
product v2003 q2003 v2004 q2004 v2005 q2005 v2006 q2006
Corn 100 20 200 25 150 30 150 10
Also . . .
USE AdventureWorks
GO
-- Creating Test Table
CREATE TABLE Product(Cust VARCHAR(25), Product VARCHAR(20), QTY INT)
GO
-- Inserting Data into Table
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','VEG',2)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','SODA',6)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','MILK',1)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','BEER',12)
INSERT INTO Product(Cust, Product, QTY)
VALUES('FRED','MILK',3)
INSERT INTO Product(Cust, Product, QTY)
VALUES('FRED','BEER',24)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','VEG',3)
GO
-- Selecting and checking entires in table
SELECT *
FROM Product
GO
-- Pivot Table ordered by PRODUCT
SELECT PRODUCT, FRED, KATE
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
PIVOT (SUM(QTY) FOR CUST IN (FRED, KATE)) AS pvt
ORDER BY PRODUCT
GO
-- Pivot Table ordered by CUST
SELECT CUST, VEG, SODA, MILK, BEER, CHIPS
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
PIVOT (SUM(QTY) FOR PRODUCT IN (VEG, SODA, MILK, BEER, CHIPS)) AS pvt
ORDER BY CUST
GO
-- Unpivot Table ordered by CUST
SELECT CUST, PRODUCT, QTY
FROM
(
SELECT CUST, VEG, SODA, MILK, BEER, CHIPS
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
PIVOT
( SUM(QTY) FOR PRODUCT IN (VEG, SODA, MILK, BEER, CHIPS)) AS pvt) p
UNPIVOT
(QTY FOR PRODUCT IN (VEG, SODA, MILK, BEER, CHIPS)
) AS Unpvt
GO
-- Clean up database
DROP TABLE Product
GO
ResultSet:
-- Selecting and checking entires in table
Cust Product QTY
------------------------- -------------------- -----------
KATE VEG 2
KATE SODA 6
KATE MILK 1
KATE BEER 12
FRED MILK 3
FRED BEER 24
KATE VEG 3
-- Pivot Table ordered by PRODUCT
PRODUCT FRED KATE
-------------------- ----------- -----------
BEER 24 12
MILK 3 1
SODA NULL 6
VEG NULL 5
-- Pivot Table ordered by CUST
CUST VEG SODA MILK BEER CHIPS
------------------------- ----------- ----------- ----------- ----------- -----------
FRED NULL NULL 3 24 NULL
KATE 5 6 1 12 NULL
-- Unpivot Table ordered by CUST
CUST PRODUCT QTY
------------------------- -------- -----------
FRED MILK 3
FRED BEER 24
KATE VEG 5
KATE SODA 6
KATE MILK 1
KATE BEER 12 12
You can even do a dynamic pivot, when you don't know, ahead of time, all the fields that you will be dealing with. Let SQL Server do the work for you!!
--Create Temporary Table #CourseSales
CREATE TABLE #CourseSales
(Course VARCHAR(50),Year INT,Earning MONEY)
GO
--Populate Sample records
INSERT INTO #CourseSales VALUES('.NET',2012,10000)
INSERT INTO #CourseSales VALUES('Java',2012,20000)
INSERT INTO #CourseSales VALUES('.NET',2012,5000)
INSERT INTO #CourseSales VALUES('.NET',2013,48000)
INSERT INTO #CourseSales VALUES('Java',2013,30000)
GO
SELECT *
FROM #CourseSales
PIVOT(SUM(Earning)
FOR Course IN ([.NET], Java)) AS PVTTable
You will find some great resources here!
http://blogs.lessthandot.com/index.php/DataMgmt/DataDesign/understanding-sql-server-2000-pivot/
http://blogs.lessthandot.com/index.php/DataMgmt/DataDesign/dynamic-pivot-on-multiple-columns/
here is the dilemma i am having...
i have 2 tables
create table #orders
(orderNumber int,qty int,sku varchar(250),barcode varchar(250))
create table #allItemsInBox
([id] int,[date] date,[localsku] varchar(250),[box] varchar(250),barcode varchar(250))
i need to join the 2 tables on [barcode] and only have 1 result in the final table for every row in #allItemsInBox
please note [#allItemsInBox].[id] is unique the other fields in [#allItemsInBox] may not be
how would i go about doing something like this?
sample data:
[#orders]
(1,0,'10','10')
(1,0,'20','20')
(3,0,'20','20')
(4,0,'30','30')
(5,0,'40','40')
(6,0,'50','50')
#allItemsInBox
(1,'12/3/2014',10,'Box1',10)
(2,'12/2/2014',20,'Box2',20)
(3,'12/1/2014',20,'Box3',20)
(4,'11/30/2014',20,'Box4',20)
(5,'11/29/2014',30,'Box5',30)
(6,'11/28/2014',40,'Box6',40)
(7,'11/27/2014',60,'Box8',60)
(8,'11/27/2014',50,'Box10',50)
#output
(ordernumber int,uniqueitemID int,localsku varchar(250),box varchar(250))
(1,1,10,'Box1')
(1,2,20,'Box2')
(3,3,10,'Box3')
(4,5,30,'Box5')
(5,6,40,'Box6')
(6,8,50,'Box10')
This is quick but works. Depending on the size of your data this might be not the best way performance wise. But this will give you a start
DECLARE #orders TABLE (
orderNumber int,
qty int,
sku varchar(250),
barcode varchar(250)
)
DECLARE #allItemsInBox TABLE (
[id] int,
[date] date,
[localsku] varchar(250),
[box] varchar(250),
barcode varchar(250)
)
INSERT INTO #orders VALUES
(1,0,'10','10'),
(1,0,'20','20'),
(3,0,'20','20'),
(4,0,'30','30'),
(5,0,'40','40'),
(6,0,'50','50')
INSERT INTO #allItemsInBox VALUES
(1,'2014-12-03',10,'Box1',10),
(2,'2014-12-02',20,'Box2',20),
(3,'2014-12-01',20,'Box3',20),
(4,'2014-11-30',20,'Box4',20),
(5,'2014-11-29',30,'Box5',30),
(6,'2014-11-28',40,'Box6',40),
(7,'2014-11-27',60,'Box8',60),
(8,'2014-11-27',50,'Box10',50)
SELECT
orders.orderNumber AS ordernumber
,(SELECT TOP 1 allItems.id FROM #allItemsInBox allItems WHERE allItems.barcode = orders.barcode AND allItems.id >= orders.orderNumber ORDER BY allItems.id) AS uniqueitemID
,(SELECT TOP 1 allItems.localsku FROM #allItemsInBox allItems WHERE allItems.barcode = orders.barcode AND allItems.id >= orders.orderNumber ORDER BY allItems.id) AS localsku
,(SELECT TOP 1 allItems.box FROM #allItemsInBox allItems WHERE allItems.barcode = orders.barcode AND allItems.id >= orders.orderNumber ORDER BY allItems.id) AS box
FROM
#orders orders
Results in:
ordernumber uniqueitemID localsku box
1 1 10 Box1
1 2 20 Box2
3 3 20 Box3
4 5 30 Box5
5 6 40 Box6
6 8 50 Box10
edit: I updated the answer. You now have the same output as you specified in your example/question
I need to copy some master-detail records, along the lines of:
INSERT INTO Order
(
SupplierId
,DateOrdered
)
SELECT
SupplierID
,DateOrdered
FROM Order
WHERE SupplierId = 10
DECLARE #OrderId int;
Select #OrderId = Scope_Identity;
INSERT INTO OrderItem
(
Quantity
,ProductCode
,Price
,FkOrderId
)
SELECT
Quantity
,ProductCode
,Price
,FkOrderId
FROM OrderItem
WHERE FkOrderId = #OrderId
This will not work. The reason is that there are multiple Orders for Supplier = 10. So what is the best way to iterate through each Order where Supplier = 10, Add the order, and then add the relevant child OrderItem BEFORE going onto the next Order Record where supplier=10. I think I am talking about batching, possibly cursors, but I am a newbie to T-SQL / Store Procedures.
I would appreciate advice on the above.
Thanks.
EDIT
Some more information which I hope will clarify by virtue of some sample data.
Original Order Table
Id SupplierId DateOrdered
1 10 01/01/2000
2 10 01/01/2000
Original OrderItem Table
Id Quantity ProductCode Price FkOrderId
1 20 X1 100 1
2 10 Y1 50 1
3 30 X1 100 2
4 20 Y1 50 2
Final Order Table
Id SupplierId DateOrdered
1 10 01/01/2000
2 10 01/01/2000
3 10 01/01/2000 (Clone of 1)
4 10 01/01/2000 (Clone of 2)
Final OrderItem Table
Id Quantity ProductCode Price FkOrderId
1 20 X1 100 1
2 10 Y1 50 1
3 30 X1 100 2
4 20 Y1 50 2
5 20 X1 100 3 (Clone of 1, linked to clone Order=3)
6 10 Y1 50 3 (Clone of 2, linked to clone Order=3)
7 30 X1 100 4 (Clone of 3, linked to clone Order=4)
8 20 Y1 50 5 (Clone of 4, linked to clone Order=4)
So I need some help with the code can do this cloning of Order and OrderItem to achieve the "final" table records.
It seems I need to do something like:
For each matching record in "Order"
Clone Order Record
Clone OrderItem Record where FkOrderId = OldOrderId
Next
This answers your question (no cursors either)
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE [Order]
(
Id Int Primary Key Identity,
SupplierId Int,
DateOrdered Date
)
SET IDENTITY_INSERT [Order] ON
INSERT INTO [Order] (Id, SupplierId, DateOrdered)
VALUES
(1, 10, '01/01/2000'),
(2, 10, '01/01/2000')
SET IDENTITY_INSERT [Order] OFF
CREATE TABLE [OrderItem]
(
ID INT Primary Key Identity,
Quantity Int,
ProductCode CHAR(2),
Price Int,
FKOrderId Int
)
SET IDENTITY_INSERT [OrderItem] ON
INSERT INTO [OrderItem] (Id, Quantity, ProductCode, Price, FKOrderId)
VALUES
(1, 20, 'X1', 100, 1),
(2, 10, 'Y1', 50, 1),
(3, 30, 'X1', 100, 2),
(4, 20, 'Y1', 50, 2)
SET IDENTITY_INSERT [OrderItem] OFF
Query 1:
DECLARE #NewEntries TABLE (ID Int, OldId Int);
MERGE INTO [Order]
USING [Order] AS cf
ON 1 = 0 -- Ensure never match - therefore an Insert
WHEN NOT MATCHED AND cf.SupplierId = 10 THEN
INSERT(SupplierId, DateOrdered) Values(cf.SupplierId, cf.DateOrdered)
Output inserted.Id, cf.Id INTO
#NewEntries(Id, OldId);
INSERT INTO [OrderItem]
(
Quantity
,ProductCode
,Price
,FkOrderId
)
SELECT
Quantity
,ProductCode
,Price
,NE.ID
FROM [OrderItem] OI
INNER JOIN #NewEntries NE
ON OI.FKOrderId = NE.OldId ;
SELECT *
FROM [OrderItem];
Results:
| ID | QUANTITY | PRODUCTCODE | PRICE | FKORDERID |
|----|----------|-------------|-------|-----------|
| 1 | 20 | X1 | 100 | 1 |
| 2 | 10 | Y1 | 50 | 1 |
| 3 | 30 | X1 | 100 | 2 |
| 4 | 20 | Y1 | 50 | 2 |
| 5 | 20 | X1 | 100 | 3 |
| 6 | 10 | Y1 | 50 | 3 |
| 7 | 30 | X1 | 100 | 4 |
| 8 | 20 | Y1 | 50 | 4 |
Add an additional column to the Order table called OriginalOrderId. Make it nullable, FK'd back to OrderId, and put an index on it. Use "INSERT INTO [Order]... SELECT ... OUTPUT INSERTED.* INTO #ClonedOrders From ...". Add an index on #ClonedOrders.OriginalOrderId. Then you can do "INSERT INTO OrderItem ... SELECT co.OrderId, ... FROM #ClonedOrders co INNER JOIN OrderItem oi ON oi.OrderId = co.OriginalOrderId". This will get you the functionality that you're looking for, along with the performance benefits of set based statements. It will also leave you evidence of the original source of the orders and a field that you can use to differentiate cloned orders from non-cloned orders.
in this case you have to use output clause.. let me give you one sample script that will help you to relate with your requirement
Declare #Order AS Table(id int identity(1,1),SupplierID INT)
DECLARE #outputOrder AS TABLE
(Orderid INT)
INSERT INTO #Order (SupplierID)
Output inserted.id into #outputOrder
Values (102),(202),(303)
select * from #outputOrder
next step for your case would be use newly generated orderid from outputorder table & join to get orderitems from input table
This will handle your first table.
PS: Supply your questions in this state and they will be answered faster.
IF OBJECT_ID('Orders') IS NOT NULL DROP TABLE Orders
IF OBJECT_ID('OrderItem') IS NOT NULL DROP TABLE OrderItem
IF OBJECT_ID('tempdb..#FinalOrders') IS NOT NULL DROP TABLE #FinalOrders
CREATE TABLE Orders (OrdersID INT, SupplierID INT, DateOrdered DATETIME)
CREATE TABLE OrderItem (OrderItemID INT, Quantity INT, FkOrderId INT)
INSERT INTO Orders VALUES (1,20,'01/01/2000'),(2,20,'01/01/2000')
INSERT INTO OrderItem VALUES
(1,20,1),
(2,10,1),
(3,30,2),
(4,20,2)
SELECT
a.OrderItemID,
b.SupplierID,
b.DateOrdered
INTO #FinalOrders
FROM OrderItem as a
INNER JOIN Orders as b
ON a.FkOrderId = b.OrdersID
SELECT * FROM #FinalOrders
This can be achieved with a cursor. But please note that cursors will pose significant performance drawbacks.
DECLARE #SupplierID AS INT
DECLARE #OrderId AS INT
DECLARE #DateOrdered AS DATE
DECLARE #OrderIdNew AS INT
Declare #Order AS Table(OrderId INT,SupplierID INT,DateOrdered Date)
INSERT INTO #Order
SELECT
ID
,SupplierID
,DateOrdered
FROM [Order]
WHERE SupplierId = 10
DECLARE CUR CURSOR FAST_FORWARD FOR
SELECT
OrderId
,SupplierID
,DateOrdered
FROM #Order
OPEN CUR
FETCH NEXT FROM CUR INTO #OrderId, #SupplierID, #DateOrdered
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO [Order]
(
SupplierId
,DateOrdered
)
VALUES
(#SupplierID,#DateOrdered)
Select #OrderIdNew=##IDENTITY
INSERT INTO [OrderItem]
([Quantity]
,[ProductCode]
,[Price]
,[FkOrderId])
SELECT [Quantity]
,[ProductCode]
,[Price]
,#OrderIdNew
FROM [OrderItem]
WHERE [FkOrderId]=#OrderId
FETCH NEXT FROM CUR INTO #OrderId, #SupplierID, #DateOrdered
END
CLOSE CUR;
DEALLOCATE CUR;
You could try doing and inner join between Order and OrderItems where the clause of the inner join is SupplierId = 10,
or just modify your where to achieve the same result.
Try doing something along the lines of:
INSERT INTO OrderItem
(
Quantity
,ProductCode
,Price
,FkOrderId
)
SELECT
Quantity
,ProductCode
,Price
,FkOrderId
FROM OrderItem
where FkOrderId in (Select Id FROM Order WHERE SupplierId = 10)
I have a question relating to trigger. I have a table (FoodPrice) with different columns like a start date and an expiry date.
Let's consider that I would like to add a price that could expire like a sale, I would like (ON INSERT) to set the ExpiryDate of the previous value to the current date:
Initial table
Food Value StartDate ExpiryDate
------ ---------- ---------- ----------
Carrot 25.5 24/12/2013 NULL
Apple 44.9 5/1/2014 NULL
Squash 25.6 12/3/2013 NULL
New table with inserted rows:
Food Value StartDate ExpiryDate
------ ---------- ---------- ----------
Carrot 25.5 24/12/2013 28/4/2014
Apple 44.9 5/1/2014 NULL
Squash 25.6 12/3/2013 28/4/2014
Carrot 24 28/4/2014 NULL
Squash 22 28/4/2014 NULL
Dupplicate values for Food column is not a big deal but is it possible to create a trigger to solve this problem ? Thank you !
Here's the code:
-- the table and sample data
create table FoodPrice (
Food varchar(10),
Value decimal(5,2),
StartDate date,
ExpiryDate date
);
go
insert FoodPrice values
('Carrot', 20, '20131124' , '20131224'),
('Apple' , 40, '20140101' , '20140105'),
('Squash', 25, '20130301' , '20130312'),
('Carrot', 25.5, '20131224' , NULL),
('Apple' , 44.9, '20140105' , NULL),
('Squash', 25.6, '20130312' , NULL)
go
-- the trigger
create trigger trFoodPrice_insert
on FoodPrice
after insert
as
;with x as (
select fp.food, fp.startdate as fp_startdate, fp.expirydate as fp_expirydate,
ins.startdate as ins_startdate, ins.expirydate as ins_expirydate,
row_number() over(partition by fp.food order by fp.startdate) as rn
from ins
inner join foodprice fp on ins.food=fp.food
and fp.startdate < ins.startdate
and fp.expirydate is null
),
y as (
select *
from x
where rn = 1
)
--select * from y
update y
set fp_expirydate = ins_startdate
go
-- let's test it
insert foodprice values
('Carrot', 24, '20140428', null),
('Squash', 22, '20140428', null)
go
select * from
foodprice
order by food, startdate
As always, I'm a big fan of first testing the select before the actual update, hence the CTE.