SQL Server 2005 Update Triggers not working for dependent columns - sql-server-2005

I have encountered a strange situation where the update trigger on a table is not updating columns that are dependent on other columns which are also getting updated during the update. Here is the background and the code for replicating this problem.
I have a commodities management application that keeps track of fruit prices everyday. I have a need to calculate the Price and Volume trend for fruits on a daily purpose. The daily Fruit prices and price volume calculations are stored in the FruitTrades table. I have defined an Update trigger on this table which will calculate the Price and Volume trend whenever a row is inserted or updated in this table.
The daily fruit prices and volume come to me in a flat file which I import into a simple Table called PriceData. Then I move the Price and Volume information from this table to the FruitTrades table using a simple INSERT statement. This fires the update triggers in the FruitTrades, but two of the columns do not get updated by the trigger. Any idea why?
Steps for replicating this problem are as follows:
-- STEP 1 (create the FruitTrades table)
CREATE TABLE [dbo].[FruitTrades](
[FID] [nchar](3) NOT NULL,
[TradeDate] [smalldatetime] NOT NULL,
[TAID] [tinyint] NULL,
[Price] [real] NOT NULL,
[Vol] [int] NULL,
[3DAvgPrice] [real] NULL,
[5DAvgPrice] [real] NULL,
[VolTrend] [real] NULL,
[VolTrendPrevD] [real] NULL,
CONSTRAINT [PK_FruitTrades] PRIMARY KEY CLUSTERED
(
[FID] ASC,
[TradeDate] DESC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];
-- STEP 2 (Create the Update trigger)
CREATE TRIGGER [dbo].[TRG_FruitTrades_Analysis]
ON [dbo].[FruitTrades]
AFTER INSERT, UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
UPDATE FruitTrades SET
-- Calculate the 3 day average price
[FruitTrades].[3DAvgPrice] =
(
SELECT AVG(Price) FROM
(
SELECT TOP 3 Price FROM FruitTrades
WHERE FID = [Inserted].[FID] AND TradeDate <= [Inserted].[TradeDate]
) AS Last3Trades
),
-- Calculate the 5 day average price
[FruitTrades].[5DAvgPrice] =
(
SELECT AVG(Price) FROM
(
SELECT TOP 5 Price FROM FruitTrades
WHERE FID = [Inserted].[FID] AND TradeDate <= [Inserted].[TradeDate]
) AS Last5Trades
),
-- Fetch the previous days VolTrend and update VolTrendPrev column
[FruitTrades].[VolTrendPrevD] =
(
SELECT TOP 1 VolTrend FROM FruitTrades
WHERE FID = [Inserted].[FID] AND TradeDate < [Inserted].[TradeDate]
),
-- Calculate Volume Trend and update VolTrend column
[FruitTrades].[VolTrend] =
(
ISNULL([FruitTrades].[VolTrendPrevD], 0) +
([Inserted].[Vol] * (([Inserted].[Price] /
(SELECT TOP 1 Price FROM FruitTrades WHERE FID = [Inserted].[FID] AND TradeDate < [Inserted].[TradeDate])) - 1.0 ))
),
-- Now Update the Action ID column
[FruitTrades].[TAID] =
(
CASE
WHEN [FruitTrades].[3DAvgPrice] >= [FruitTrades].[5DAvgPrice] AND [FruitTrades].[VolTrend] >= [FruitTrades].[VolTrendPrevD] THEN 1
WHEN [FruitTrades].[3DAvgPrice] >= [FruitTrades].[5DAvgPrice] AND [FruitTrades].[VolTrend] <= [FruitTrades].[VolTrendPrevD] THEN 2
WHEN [FruitTrades].[3DAvgPrice] <= [FruitTrades].[5DAvgPrice] AND [FruitTrades].[VolTrend] >= [FruitTrades].[VolTrendPrevD] THEN 3
WHEN [FruitTrades].[3DAvgPrice] <= [FruitTrades].[5DAvgPrice] AND [FruitTrades].[VolTrend] <= [FruitTrades].[VolTrendPrevD] THEN 4
ELSE NULL
END
)
FROM FruitTrades
INNER JOIN Inserted ON Inserted.FID = FruitTrades.FID AND Inserted.TradeDate = FruitTrades.TradeDate
END
-- STEP 3 (Create the PriceData table)
CREATE TABLE [dbo].[PriceData](
[FID] [nchar](3) NOT NULL,
[TradeDate] [smalldatetime] NOT NULL,
[Price] [real] NULL,
[Vol] [real] NULL,
CONSTRAINT [PK_PriceData] PRIMARY KEY CLUSTERED
(
[FID] ASC,
[TradeDate] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
-- STEP 4 (simulate data import into PriceData table)
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/30/2012', 200, 1000);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/29/2012', 190, 1200);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/28/2012', 195, 1250);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/27/2012', 205, 1950);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/26/2012', 200, 2000);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/25/2012', 180, 1300);
INSERT INTO PriceData (FID, TradeDate, Price, Vol) VALUES ('APL', '4/24/2012', 185, 1250);
-- STEP 5 (move price vol date from PriceDate table to Fruit Tables)
INSERT INTO FruitTrades (FID, TradeDate, Price, Vol) SELECT FID, TradeDate, Price, Vol FROM PriceData;
-- STEP 6 (check the FruitTrades table for correctness)
SELECT * FROM FruitTrades ORDER BY TradeDate
--- Results
After Step 6 you will find that the TAID and VolTrendPrevD in the FruitTrades table columns remain NULL.
Any help on how to resolve this problem is highly appreciated.

After laboring on this problem for 5 days, and some Googling, I finally found the solution myself.
The first problem was on account of my lack of understanding of triggers and how they fire. As per SQL documentation,
SQL Server triggers fire only once per statement, not once per
affected row
Due to this design principle, when in Step 5, I do a bulk Insert from the PriceData table into the FruitTrades table, the trigger is fired only once, instead of once for each row. Hence the Updated values are incorrect.
The VolTrendPrevD remains null because the Select statement for it in the Update trigger always matches the first row in the FruitTrades table (since the Inserted table has multiple rows) and for this row, the VolTrend is null.
The TAID remains null because VolTrendPrevD is null.
Now the fix:
Import the text file containing the price data into a MSaccess table. From there do a bulk insert in to the SQL Server table (using Linked tables). This approach uses ODBC to convert the bulk insert into multiple single inserts, thus bypassing the first problem.
Convert the VolTrend into a computed column. There is no need to update it therefore in the trigger.
Introduce an additional column PricePrevD in the FruitTrades table and update its value in the trigger, in the same manner as the VolTrendPrevD column.
Most importantly, ensure that the inserted rows from Access are inserted in ascending order by date (by creating an appropriate date index in Access). Else the desired results will be missing.
Hope this helps... :-)

Related

Stored procedure to Add Multiple Records at once by generating custom column value based on inputs

For SQL Server 2016 I am trying to write stored procedure to add multiple records at once by generating custom column value based on inputs with description given below
Following is my table definition for SQL Server 2016
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Box]
(
[BoxId] [nvarchar](20) NOT NULL,
[HouseId] [nvarchar](20) NOT NULL,
[Status] [nchar](10) NULL,
CONSTRAINT [PK_Box]
PRIMARY KEY CLUSTERED ([BoxId] ASC, [HouseId] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Another table used is House with following details
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[House]
(
[HouseId] [nvarchar](20) NULL,
[Prefix] [nvarchar](20) NULL,
[Address] [nvarchar](50) NULL
) ON [PRIMARY]
GO
Tasks is to insert records into Box table. SQL query will be converted to a stored procedure. There is no identity column and autoincrement. HouseId is available as input and number of records to insert (BoxCount) as input and BoxId is to generated for each insert.
BoxID column has the format of Prefix + Number; Prefix is to be referred to from the House table), Number should be auto incremented by one than previous record.
Sample values for BoxId columns are
WOOO1-1
WOOO1-2
WOOO1-1000
WOOO1-1004
The stored procedure defined is
CREATE PROCEDURE usp_Insert_Boxes
#HouseId nvarchar(20),
#BoxCount int /* Numbers of Box records to insert*/
AS
BEGIN
DECLARE #BoxId = "?"
INSERT INTO [dbo].[Box] ([BoxId], [HouseId], [Status])
VALUES
/*BoxId to be generated. BoxId has format Prefix-Number */
(#BoxId, #HouseId, 'Open')
END
Query/stored procedure is to be written to insert n number of records into Box table when input available is HouseId, and number of records to inserts (prefix to be taken from the House table). Status value is always 'Open'.
For example for House table data as below
HouseId Prefix Address
----------------------------
1 W0001 Address1
2 W0002 NULL
This is the expected output for HouseId = 1 and BoxCount = 5 input to proposed stored procedure:
BoxId HouseId Status
---------------------------
W0001-1 1 Open
W0001-2 1 Open
W0001-3 1 Open
W0001-4 1 Open
W0001-5 1 Open
What changes are needed to the stored procedure to get desired functionality? The stored procedure will be called from C# Winforms application using System.Data.SqlClient.
Edit 1
Based on answer provided by Aaron Bertrand I have updated stored procedure as below
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[usp_Insert_Boxes]
#HouseId nvarchar(20),
#BoxPrefix nvarchar(5),
#BoxCount int /* Numbers of Box records to insert*/
AS
BEGIN
INSERT INTO [dbo].[Box]
([BoxId]
,[HouseId]
,[Status])
SELECT TOP (#BoxCount) [BinId] = CONCAT(#BoxPrefix, '-',
ROW_NUMBER() OVER (ORDER BY [object_id])),
[HouseId] = #HouseId ,
[Status] = N'Open'
FROM sys.all_columns
ORDER BY [object_id];
END
and executes it as shown below
DECLARE #return_value int
EXEC #return_value = [dbo].[usp_Insert_Boxes]
#HouseId = N'1',
#BoxPrefix = N'W0001',
#BoxCount = 5
SELECT 'Return Value' = #return_value
GO
Although it works for first execution, for second invocation it it shows following error
State 1, Procedure usp_Insert_Boxes, Line 11 [Batch Start Line 2]
Violation of PRIMARY KEY constraint 'PK_Box'. Cannot insert duplicate key in object 'dbo.Box'. The duplicate key value is (W0001-1, 1).
However requirement is that stored procedure should check whether HouseId and BoxId combination does not already exists and if exists if needs to generate new set of non existing incremental BoxIds for insertion. Also for each HouseId, Number part of BoxId should start from 1 and then incrementally onwards. May be I have not stressed this part enough in my question.
BoxId HouseId Status
W0001-1 1 Open
W0001-2 1 Open
W0001-3 1 Open
W0001-4 1 Open
W0001-5 1 Open
W0002-1 2 Open
W0002-2 2 Open
W0002-3 2 Open
W0002-4 2 Open
W0002-5 2 Open
A couple of straightforward ways to generate a set given an integer value (at least within reason):
DECLARE #i int = 5;
SELECT TOP (#i) n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_columns
ORDER BY [object_id];
Or a recursive CTE:
DECLARE #i int = 5;
;;;/* leading semi-colons are intentional */;;;WITH cte(n) AS
(
SELECT 1
UNION ALL SELECT n + 1 FROM cte WHERE n < #i
)
SELECT n FROM cte;
(If #i can be > 100 you'll need OPTION (MAXRECURSION hint.)
Both of these produce this output:
n
1
2
3
4
5
Example db<>fiddle
So you could mix either of those that with whatever values go on every row, e.g.
DECLARE #i int = 5; -- input param
-- determined inside the procedure?
DECLARE #BoxPrefix nvarchar(20) = N'W0001-';
--INSERT dbo.[Box](BoxId,HouseId,[Status])
SELECT TOP (#i) BoxId = CONCAT(#BoxPrefix,
ROW_NUMBER() OVER (ORDER BY [object_id])),
HouseId = 1,
[Status] = N'Open'
FROM sys.all_columns
ORDER BY [object_id];
This produces:
BoxId
HouseId
Status
W0001-1
1
Open
W0001-2
1
Open
W0001-3
1
Open
W0001-4
1
Open
W0001-5
1
Open
Example db<>fiddle

Update Query - Sum of previous row x and current row y

I having a table LedgerData and need to update the Balance.
Table Structure
CREATE TABLE [dbo].[LedgerData]
(
[Id] INT NOT NULL,
[CustomerId] INT NOT NULL,
[Credit] INT,
[Debit] INT,
[Balance] INT
CONSTRAINT [PK_dbo_LedgerData]
PRIMARY KEY CLUSTERED ([Id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];
Sample Data
INSERT INTO [dbo].[LedgerData] VALUES (1, 1, 50, 0, 0);
INSERT INTO [dbo].[LedgerData] VALUES (2, 1, 0, 25, 0);
INSERT INTO [dbo].[LedgerData] VALUES (3, 2, 0, 75, 0);
INSERT INTO [dbo].[LedgerData] VALUES (4, 1, 0, 10, 0);
INSERT INTO [dbo].[LedgerData] VALUES (5, 2, 5, 0, 0);
INSERT INTO [dbo].[LedgerData] VALUES (6, 1, 10, 25, 0);
I tried to update the balance column customer wise ORDER BY [Id] ASC, but its not updating as expected. Also I explored the sql query to calculate sum and add sum from previous rows
Please assist me to calculate the balance column Balance = (Previous Row Balance + Credit - Debit)
Ideally this is something you should be doing as you INSERT the data, by getting the previous value (and locking the table so that other INSERT statements can't occur to avoid races) and then supplying a value for the Balance. You can, however, UPDATE all the rows with a cumulative SUM and an updatable CTE:
WITH CTE AS(
SELECT ID,
CustomerID,
Balance,
0 + SUM(Credit) OVER (PARTITION BY CustomerID ORDER BY ID) - SUM(Debit) OVER (PARTITION BY CustomerID ORDER BY ID) AS NewBalance
FROM dbo.LedgerData)
UPDATE CTE
SET Balance = NewBalance;
GO
SELECT *
FROM dbo.LedgerData;
Alternatively, don't store the aggregate value at all, and use a VIEW so that the value can always be calculated (accurately) with the same expression I have used in the CTE. For example:
CREATE VIEW dbo.LedgerDataCumulative
AS
SELECT Id,
CustomerId,
Credit,
Debit,
SUM(Credit) OVER (PARTITION BY CustomerID ORDER BY ID) - SUM(Debit) OVER (PARTITION BY CustomerID ORDER BY ID) AS Balance
FROM dbo.LedgerData;
GO
The update can be performed with a single window function.
with upd_cte as (
select *, sum([Credit]-[Debit]) over (partition by customerId order by id) sum_over
from #LedgerData)
update upd_cte
set Balance=sum_over;

SQL Server: update rows that take part in grouping

I'm using SQL Server 2017 (v14.0).
I have two tables with one-to-many relationship. I need to group the rows in the "Orders" table and by this info create the row in the "Transactions" table, then I need set the relationship - for a created transaction I need set the TransactionId to related order's rows in one query/transaction flow to keep the consistency.
I correctly insert the new row by grouped data, but can't update the related rows in "Orders" table to set the relationship (TransactionId) for related "Transactions".
Can you, please, help with composing the query statement or get the clue to move in the right direction?
CREATE TABLE [dbo].[Orders]
(
[OrderId] INT NOT NULL,
[TransactionId] INT NULL,
[OrderVolume] DECIMAL(18, 8) NOT NULL,
[OrderCurrencyId] INT NOT NULL,
)
CREATE TABLE [dbo].[Transactions]
(
[TransactionId] INT NULL,
[Volume] DECIMAL(18, 8) NOT NULL,
)
INSERT INTO Transactions (Volume)
OUTPUT INSERTED.[TransactionId] --also need to update the rows in "Orders" that take a part in grouping to set the relationship
SELECT
SUM(OrderVolume) AS OrderVolume,
FROM Orders
GROUP BY Orders.OrderCurrencyId
The problem with the OUTPUT clause in an INSERT statement is that it doesn't allow you to select any field from the source table.
You can achieve this using MERGE statement instead:
DECLARE #t TABLE([TransactionId] INT, [OrderCurrencyId] INT)
MERGE Transactions trgt
USING
(
SELECT
SUM(OrderVolume) AS OrderVolume
, Orders.OrderCurrencyId AS OrderCurrencyId
FROM
Orders
GROUP BY
Orders.OrderCurrencyId
) AS src ON (1=0)
WHEN NOT MATCHED THEN
INSERT ( [Volume] ) VALUES (src.OrderVolume)
OUTPUT [inserted].[TransactionId], src.[OrderCurrencyId]
INTO #t;
UPDATE Orders
SET TransactionId = t.TransactionId
FROM Orders
JOIN #t t ON Orders.OrderCurrencyId = t.OrderCurrencyId
Demo here

How To Get A Hierarchical CTE In SQL Server To Filter With Parent and Child Logic

I'm having a vexing problem with a hierarchical CTE and some strange logic that we need to address that I really hope someone could assist with pointing out what I'm doing wrong to address this scenario with a CTE.
Here is the hierarchical data we're dealing with in this example:
This is the problematic SQL followed by the description of the problem and SQL statements to create a test table with data:
DECLARE #UserId nvarchar(50);
SET #UserId = 'A';
DECLARE #StatusType int;
SET #StatusType = '2';
;WITH recursiveItems (Id, Depth)
AS
(
SELECT Id, 0 AS Depth
FROM dbo.CteTest
WHERE UserId = #UserId
--AND StatusType = #StatusType
-- This would also be incorrect for the issue
AND ParentId IS NULL
UNION ALL
SELECT dbo.CteTest.Id, Depth + 1
FROM dbo.CteTest
INNER JOIN recursiveItems
ON dbo.CteTest.ParentId = recursiveItems.Id
WHERE UserId = #UserId
AND StatusType = #StatusType
)
SELECT A.*, recursiveItems.Depth
FROM recursiveItems
INNER JOIN dbo.CteTest A WITH(NOLOCK) ON
recursiveItems.Id = A.Id
ORDER BY A.Id
This is not returning the desired data. The data that is currently returned is in the NOT CORRECT section of the image below. The row with the Id of 10 is the row that we want to omit.
Essentially the logic should be that any parent record (record with children) where the status type of any of its children is equal to 2 should be returned along with its children. In the example this is the rows with Ids: 1, 5, 6, 7, 9.
Currently the CTE/SQL/Code is returning ALL parent records no matter what,
The record with the Id 1 should be returned, even though it's status type is 1 because at least one of its children, their children, grandchildren, etc. have a status type that is equal to 2.
The record with the Id of 10 should not be returned because it does not have a status that is equal to 2 or any children. If the record had a status type of 2 when it has no child records it should also be returned.
This is the DDL to create a test table that helps to show the problem:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[CteTest](
[Id] [int] IDENTITY(1,1) NOT NULL,
[StatusType] [int] NOT NULL,
[UserId] [nvarchar](50) NOT NULL,
[ParentId] [int] NULL,
CONSTRAINT [PK_CteTest] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
This is the seed data for the table, that can demonstrate the issue:
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (1,'A',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (1,'B',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'B',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (1,'A',1)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'A',1)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'A',5)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'A',6)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (3,'A',6)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'A',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (4,'A',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (3,'A',10)
The issue is that your base case includes all null (parentless) items, and there is no way to filter them out later.
Because you are looking for only items with a particular statustype, you may want to refactor the CTE; Instead of having a base case be the root values, you can have it be all items with the given statustype, and then recursively find the parents. In the solution below, I have depth be a negative number, for distance from the item with a value of 2 in the given tree (so negative height, instead of depth.).
DECLARE #UserId nvarchar(50);
SET #UserId = 'A';
DECLARE #StatusType int;
SET #StatusType = '2';
WITH recursiveItems (Id, ParentID, Depth)
AS
(
SELECT Id, ParentID, 0 AS Depth
FROM dbo.CteTest
WHERE UserId = #UserId AND StatusType = #StatusType
UNION ALL
SELECT dbo.CteTest.Id, CteTest.ParentID, Depth - 1
FROM dbo.CteTest
INNER JOIN recursiveItems
ON dbo.CteTest.Id = recursiveItems.ParentId
WHERE UserId = #UserId
)
SELECT A.Id, A.StatusType, A.UserId, A.ParentId, min(recursiveItems.Depth)
FROM recursiveItems
INNER JOIN dbo.CteTest A WITH(NOLOCK) ON
recursiveItems.Id = A.Id
group by A.Id, A.StatusType, A.UserId, A.ParentId
ORDER BY A.Id

Calculate Percentage Applied Sql server 2008

A wrong percentage has been applied to a field (TotalPercentageAmount) and I need to correct it.
Given 2 fields Amount and TotalPercentageAmount how can I calculate what percentage was applied?
I need to work out percentage applied to TotalPercentageAmount and UPDATE the column with correct percentage.
Little script I have created to mimin my scenario . Table created contains wrong TotalPercentageAmount!!!
CREATE TABLE [dbo].[SalesReportTest](
[Id] [int] NOT NULL,
[Amount] [decimal](18, 4) NOT NULL,
[TotalPercentageAmount] [decimal](18, 4) NOT NULL,
CONSTRAINT [PK_SalesReportTest] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET NOCOUNT ON;
SET XACT_ABORT ON;
GO
BEGIN TRANSACTION;
INSERT INTO [dbo].[SalesReportTest]([Id], [Amount], [TotalPercentageAmount])
SELECT 1, 55.0000, 52.0300 UNION ALL
SELECT 2, 440.0000, 416.2200 UNION ALL
SELECT 3, 300.0000, 283.8000 UNION ALL
SELECT 4, -55.0000, -52.0300 UNION ALL
SELECT 5, 98.0000, 92.7000 UNION ALL
SELECT 6, -10.0000, -9.4600
COMMIT;
RAISERROR (N'[dbo].[SalesReportTest]: Insert Batch: 1.....Done!', 10, 1) WITH NOWAIT;
GO
You can try the following to determine the percentage used. However, it appears that there is some precision loss because the Amount column appears to be truncated. I also include a column that demonstrates the 5.72% calc in your sample data.
SELECT *,
CAST ((Amount / TotalPercentageAmount - 1) * 100 AS DECIMAL (5, 2)) as Pct,
CAST (TotalPercentageAmount * 1.0572 AS INT) Amt_Calc
FROM [SalesReportTest]
If I understand you correctly, you can update the percentages like:
update SalesReportTest
set TotalPercentageAmount = Amount /
(select sum(amount) from SalesReportTest)