Calculate Percentage Applied Sql server 2008 - sql

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)

Related

Filter by Id - Exact match or get the default record

Filter the table by exact match if the match is not exist filter by default Id. Consider the following Table:
Table structure:
CREATE TABLE [dbo].[ProductGroupData]
(
[Id] TINYINT NOT NULL,
[Name] NVARCHAR(50) NOT NULL,
CONSTRAINT [PK_dbo_ProductGroupData]
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];
CREATE TABLE [dbo].[ProductData]
(
[Id] INT NOT NULL,
[GroupId] TINYINT NOT NULL,
[TypeId] TINYINT NOT NULL,
[Product] NVARCHAR(50) NOT NULL,
CONSTRAINT [PK_dbo_ProductData]
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].[ProductGroupData] VALUES (1, N'Apple Box');
INSERT INTO [dbo].[ProductGroupData] VALUES (2, N'Orange Box');
INSERT INTO [dbo].[ProductData] VALUES (1, 1, 1, N'Apple #1');
INSERT INTO [dbo].[ProductData] VALUES (2, 1, 3, N'Apple #3');
INSERT INTO [dbo].[ProductData] VALUES (3, 1, 4, N'Apple #4');
INSERT INTO [dbo].[ProductData] VALUES (4, 1, 5, N'Apple #5');
INSERT INTO [dbo].[ProductData] VALUES (5, 2, 1, N'Orange #1');
INSERT INTO [dbo].[ProductData] VALUES (6, 2, 5, N'Orange #5');
The [TypeId] rages from 1 to 5 and the default [TypeId] is 1; If the match is not exist need to return the result with filter [TypeId] is 1 in the Same SELECT statement. Here I projected the table in the explanatory purpose but in actual scenario I used this logic in INNER JOIN
I tried with the following scenarios
Scenario #1:
DECLARE #GroupId TINYINT = 1;
DECLARE #DefaultTypeId TINYINT = 1;
DECLARE #TypeId TINYINT = 2;
SELECT PD.*
FROM [dbo].[ProductGroupData] PGD
INNER JOIN [dbo].[ProductData] PD ON PD.[GroupId] = PGD.[Id]
WHERE PD.[GroupId] = #GroupId
AND (PD.[TypeId] = #TypeId OR PD.[TypeId] = #DefaultTypeId);
Scenario #2:
DECLARE #TypeId TINYINT = 3;
This above statement i.e., Scenario #1 works fine for the missing Id's and the default Id, If I tried the Scenario #2 SELECT statement it returns two rows:
The expected result is
How to perform this in a Single SELECT Statement. Please assist.
You can use the following query:
;WITH CTE AS
(
SELECT ID FROM [dbo].[ProductData] WHERE [GroupId] = #GroupId AND [TypeId] = #TypeId
)
SELECT PD.*
FROM [dbo].[ProductGroupData] PGD
INNER JOIN [dbo].[ProductData] PD ON PD.[GroupId] = PGD.[Id]
WHERE
(PD.ID IN (SELECT ID FROM CTE))
OR
(NOT EXISTS(SELECT 1 FROM CTE) AND PD.[GroupId] = #GroupId AND PD.[TypeId] = #DefaultTypeId)
The first filter (exact match) is applied by the CTE. The second filter (default record) is applied by the main query only when CTE returns nothing.

Table not updating in single select query

i have a temporary table which i need to update, the first row is updated but the second row updates as null , please help
declare #T Table
(
ID int,
Name nvarchar(20),
rownum int
)
insert into #T(ID,rownum)
select ID, rownum = ROW_NUMBER() OVER(order by id) from testtabel4
select * from testtabel4
update #t
set Name=case when rownum>1 then (select top 1 Name from #T x where x.rownum=(y.rownum-1))
else 'first' end
from #t y
select * from #T
and here the definition of testtabel4
CREATE TABLE [dbo].[testtabel4](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](80) NULL,
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
and here is the output
ID Name
1 first
2 NULL
I think your update would be better written with lag() and an updateable CTE.
with cte as (
select name, lag(name, 1, 'first') over(order by rownum) lag_name
from #t
)
update cte set name = lag_name
With this technique at hand, it is plain to see that don't actually need to feed the table first, then insert into it. You can do both at once, like so:
insert into #t (id, name, rownum)
select
id,
lag(name, 1, 'first') over(order by id),
row_number() over(order by id)
from testtabel4
I am not sure that you even need rownum column anymore, unless it is needed for some other purpose.
You are only inserting two columns in the #T:
insert into #T (ID, rownum)
select ID, ROW_NUMBER() OVER (order by id)
from testtabel4;
You are not inserting name so it is NULL on all rows. Hence, the then part of the case expression will always be NULL.

SQL Server 2005 Update Triggers not working for dependent columns

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... :-)

Index is not getting applied on Indexed View

I have an indexed view but when I run queries on that view the index which is built on View is not applied and the query runs without index. Below is my dummy script:
Tables + View+ Index on View
CREATE TABLE P_Test
(
[PID] INT IDENTITY,
[TID] INT,
[StatusID] INT
)
CREATE TABLE T_Test
(
[TID] INT IDENTITY,
[FID] INT,
)
CREATE TABLE F_Test
(
[FID] INT IDENTITY,
[StatusID] INT
)
GO
INSERT INTO F_Test
SELECT TOP 1000 ABS(CAST(NEWID() AS BINARY(6)) %10) --below 100
FROM master..spt_values
INSERT INTO T_Test
SELECT TOP 10000 ABS(CAST(NEWID() AS BINARY(6)) %1000) --below 1000
FROM master..spt_values,
master..spt_values v2
INSERT INTO P_Test
SELECT TOP 100000 ABS(CAST(NEWID() AS BINARY(6)) %10000) --below 10000
,
ABS(CAST(NEWID() AS BINARY(6)) %10)--below 10
FROM master..spt_values,
master..spt_values v2
GO
CREATE VIEW [TestView]
WITH SCHEMABINDING
AS
SELECT P.StatusID AS PStatusID,
F.StatusID AS FStatusID,
P.PID
FROM dbo.P_Test P
INNER JOIN dbo.T_Test T
ON T.TID = P.TID
INNER JOIN dbo.F_Test F
ON T.FID = F.FID
GO
CREATE UNIQUE CLUSTERED INDEX [PK_TestView]
ON [dbo].[TestView] ( [PStatusID] ASC, [FStatusID] ASC, [PID] ASC )
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Now when I run the following queries the [PK_TestView] index is not being applied:
SELECT PStatusID ,
FStatusID ,
PID FROM [TestView]
SELECT PStatusID ,
FStatusID ,
PID FROM [TestView]
WHERE [PStatusID]=1
SELECT COUNT(PStatusID) FROM [TestView]
WHERE [PStatusID]=1
Can you help me fixing this?
You need to use the NOEXPAND hint. SQL Server will not consider matching indexed views without this (even if the view name is referenced in the query) unless you are on Enterprise Edition engine.
SELECT COUNT(PStatusID)
FROM [TestView]
WITH (NOEXPAND) -- this line
WHERE [PStatusID]=1
This should give you the first, much cheaper, plan

Filtering data using two columns in T-SQL

I have files that are versioned, using a major and minor version.
Now what I want to do is grab the highest major versions of all the files (which I've done already). Then check minor versions to see if it contains a 0. If it does throw out that whole set of versions. So for instance. My first query returns:
FileA Ver 1.0, 1.1, 1.2, 1.3
FileB Ver 2.1, 2.2, 2.3, 2.4
FileC Ver 5.1, 5.2, 5.3.
So in all 11 rows/records. Now my second query should take that result and throw out all of the FileA versions because one of the versions has a 0 as a minor version. So the second query should return:
FileB Ver 2.1, 2.2, 2.3, 2.4
FileC Ver 5.1, 5.2, 5.3.
7 rows/records in all.
Could anyone help me with this query? I'm using SQL server 2008 if that helps.
latestFileMajorVersion(fileId, majRev)
as
(
--Gets files with highest major version
select distinct v_fileid, max(v_majrev)
from files
group by v_fileid
),
latestFileVersions(fileId, majRev, minRev)
as
(
--Gets files with highest major version and all minor versions
select fileId, majRev, minrev
from files
inner join latestFileMajorVersion on files.v_fileid = latestFileMajorVersion.fileId
where v_majrev = latestFileMajorVersion.majRev
),
latestFileVersion(fileId, majRev, minRev)
as
(
select fileId, majRev, minrev
from latestFileVersions
where --I'm stuck here.
)
select v_fileid, v_majrev, v_minrev, v_status
from files
inner join latestFileVersion on v_fileid = latestFileVersion.fileId
where latestFileVersion.majRev = v_majrev and latestFileVersion.minRev = v_minrev and v_status = 'UnAssigned'
The table:
CREATE TABLE [dbo].[Files](
[v_fileid] [uniqueidentifier] NOT NULL,
[v_libid] [uniqueidentifier] NOT NULL,
[v_userid] [uniqueidentifier] NOT NULL,
[v_majrev] [int] NOT NULL,
[v_minrev] [int] NOT NULL,
[v_status] [nvarchar](16) NOT NULL,
CONSTRAINT [PK_Files] PRIMARY KEY CLUSTERED
(
[v_fileid] ASC,
[v_majrev] ASC,
[v_minrev] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [IX_Files] UNIQUE NONCLUSTERED
(
[v_majrev] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [IX_Files] UNIQUE NONCLUSTERED
(
[v_minrev] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
You can rewrite your query using this idea for the syntax. It works for the sample data you provided.
declare #t table (fileid varchar(10), [version] varchar(5))
insert #t values('FileA','1.0')
insert #t values('FileA','1.1')
insert #t values('FileA','1.2')
insert #t values('FileA','1.3')
insert #t values('FileB','2.1')
insert #t values('FileB','2.2')
insert #t values('FileB','2.3')
insert #t values('FileB','2.4')
insert #t values('FileC','5.1')
insert #t values('FileC','5.2')
insert #t values('FileC','5.3')
SELECT * FROM #t t
WHERE NOT EXISTS
(SELECT 1 FROM #t WHERE PARSENAME([version], 1) = '0' AND t.fileid = fileid)
Here's a working solution with a little example table you can play with. Execute it exactly as it is with no other sql around it. You can then modify it to suit your need.
declare #files table (fileId int identity(1,1), majRev int, minRev int)
insert into #files values (1,0)
insert into #files values (1,1)
insert into #files values (2,0)
insert into #files values (2,2)
insert into #files values (3,1)
insert into #files values (3,2)
insert into #files values (4,1)
select fileId, majRev, minRev
from #files f
where not exists
(select *
from #files
where minRev = 0
and majRev = f.majRev
and fileId = f.fileId)
When I try to code something, sometimes I get too caught up with making an "elegant" solution, as opposed to just brute-forcing the solution and having it apply only to a particular problem.
So I'm wondering if 1) I've brute-forced this solution and 2) is there a more "elegant" or clever way to go about writing this. All comments, critiques, and/or suggestions are welcome.
;with solution(fileId, majrev, minrev, status, checkid, maxrev)
as
(
select *
from files
inner join
(
select v_fileid, MAX(v_majrev) as max_major
from files
group by v_fileid
) as max_versions on max_versions.v_fileid = files.v_fileid and
max_major = files.v_majrev
except
select *
from files
inner join
(
select v_fileid, MAX(v_majrev) as max_major
from files
where v_minrev = 0
group by v_fileid
) as unwanted_versions on unwanted_versions.v_fileid = files.v_fileid and
max_major = files.v_majrev
)
select *
from solution