Create table and get data from another table - sql

I have a table Cost category:
CREATE TABLE [dbo].[CostCategory](
[ID_CostCategory] [int] NOT NULL,
[Name] [varchar](150) NOT NULL,
[Plan] [money] NOT NULL,
[Realization] [money] NULL,
)
go
and I have another table with defined Costs:
CREATE TABLE [dbo].[Cost](
[ID_Cost] [int] NOT NULL,
[Name] [varchar](50) NULL,
[ID_CostCategory] [int] NULL,
[ID_Department] [int] NULL,
[ID_Project] [int] NULL,
[Value] [money] NULL,
)
go
What I want to do is to sum values from Cost table (according to ID_CostCategory) and put into Cost Category table, Realization column. So each ID_CostCategory presents automaticaly sum of Costs from Cost table (per ID_CostCategory).
How can I modify the script of Cost Category table to achieve it? Probably it's not a rocket science but I am really new to SQL.
OK. One more thing I forgot to add...
the structure looks in a way:
Cost>Cost Category>Department>Project
Right now I can easily take data from cost table and present it in Cost Category table. But Cost table includes such data as:
ID_Cost Name ID_CostCategory ID_Department ID_Project Value
1 fv 001 1 1 1 100
2 fv 002 2 1 1 500
3 fv 003 2 2 1 300
4 fv 004 3 2 2 150
5 fv 005 3 3 2 30
6 fv 006 4 3 2 15
I have also table Department which includes colums: ID_Department, Name, Plan,
So now, I want to do is to sum values from Cost table (according to ID_CostCategory and ID_Department) and put into Department table as Realization column. So each ID_Department presents automaticaly sum of Costs from Cost table (per ID_CostCategory and ID_Department).
Hope it is clear (later on will have to do it with table Project, but once I got it, it will be easy)

Try this
--create table without realization column
CREATE TABLE [dbo].[CostCategory](
[ID_CostCategory] [int] NOT NULL,
[Name] [varchar](150) NOT NULL,
[Plan] [money] NOT NULL
) go
CREATE TABLE [dbo].[Cost](
[ID_Cost] [int] NOT NULL,
[Name] [varchar](50) NULL,
[ID_CostCategory] [int] NULL,
[ID_Department] [int] NULL,
[ID_Project] [int] NULL,
[Value] [money] NULL,
) go
Create a UDF to calculate sum of the cost column:
CREATE FUNCTION [dbo].[CalculateRealization](#Id INT)
RETURNS money
AS
BEGIN
DECLARE #cost money
SELECT #cost = SUM(Value)
FROM [dbo].[Cost]
WHERE [ID_CostCategory] = #ID
return #cost
END
Now Alter your CostCategory table to add computed column:
ALTER TABLE [dbo].[CostCategory]
ADD [Realization] AS dbo.CalculateRealization(ID_CostCategory);
Now you can select Realization from Costcategory
SELECT ID_CostCategory, Realization
FROM [dbo].[CostCategory]
Answer to your comment below:
Create Another UDF
CREATE FUNCTION [dbo].[CheckValue](#Id INT, #value Money)
RETURNS INT
AS
BEGIN
DECLARE #flg INT
SELECT #flg = CASE WHEN [Plan] >= #value THEN 1 ELSE 0 END
FROM [dbo].[CostCategory]
WHERE [ID_CostCategory] = #ID
return #flg;
END
Now add Constraint on Cost Table:
ALTER TABLE ALTER TABLE [dbo].[Cost]
ADD CONSTRAINT CHK_VAL_PLAN_COSTCATG
CHECK(dbo.CheckValue(ID_CostCategory, Value) = 1)

You do not need to have a Realization column as part of the CostCategory table. Rather, you will want to use a join.
Select A.ID_CostCategory, A.Name, SUM(B.Value) As Realization from CostCategory A
JOIN Cost B ON A.ID_CostCategory = B.ID_CostCategory
Group By A.ID_CostCategory, A.Name

Related

replace a computed column with a logic that works with INSERT

I have a table called tblPacks.
CREATE TABLE [dbo].[tblPacks]
(
[ID] [int] NOT NULL,
[BatchNumber] [varchar](30) NULL,
[PackID] VARCHAR(50),
[Status] [int] NULL
)
And a stored procedure spInsertPacks.
CREATE PROCEDURE spInsertPacks
#ID INT,
#BatchNumber VARCHAR(30),
#Count INT
AS
BEGIN
INSERT INTO tblPacks
Values
(
#ID,
#BatchNumber,
CONVERT([varchar](50),
'PK'+
case
when len(#ID)<=(3) then CONVERT([varchar](20),right((0.001)*#ID,(3)),0)
else CONVERT([varchar](20),#ID,0)
end,0),0)
END
If ID of data type INT inserted in an order like 1,2,3,4,5... the above logic works fine. But there is no restriction for a user to enter random numbers. I want a stored procedure to generate PackID(PK001,PK002..) sequence in order, irrespective of #ID and ID. Cannot be an identity Column. How can I do that?
Actually This PackID is a barcode If barcode already existed for Pack then that sequence may not be same with the sequence we used and Newly generated barcodes which we are generating will be in seuquence PK001
Sample Output:-
ID BatchNumber PackID Status
1 b1 PK001 0
1 b2 Pk002 0
5 b7 ABC768 0
3 b2 PK003 0
I have simplified the logic a bit for generating PackID
Add a new column(identifier) for identifying the code and use it for PackID generation and for sequence use Identity column
CREATE TABLE [dbo].[tblPacks]
(
Iden_ID INT IDENTITY(1, 1),
[ID] [INT] NOT NULL,
[BatchNumber] [VARCHAR](30) NULL,
[Identifier] [VARCHAR](50),
[PackID] AS [Identifier]
+ CASE
WHEN Iden_ID <= 999 THEN RIGHT('00' + CONVERT(VARCHAR(3), ID), 3)
ELSE CONVERT([VARCHAR](20), ID, 0)
END,
[Status] [INT] NULL
)
To check the working
INSERT INTO [dbo].[tblPacks]
([ID],identifier,[BatchNumber],[Status])
VALUES (1,'pk','bat',1)
SELECT *
FROM [tblPacks]

How can join 2 Table when Key are in different columns

i have 2 Tables: accounthierarchy and accountvaluetotal.
the link between 2 Tables is account number. i want to join the table based on account number. But the account number of table "account hierarchy " is on different Level (column).
Can you please help me how to do it? Thanks
CREATE TABLE [dbo].[accounthierarchy](
[ID] [int] NULL,
[level1] [int] NULL,
[level2] [int] NULL,
[level3] [int] NULL,
[level4] [int] NULL,
[level5] [int] NULL)
INSERT INTO [dbo].[accounthierarchy] (ID,level1,Level2,level3,level4,level5)
VALUES
(1,100,null,null,null,null),
(2,100,110,null,null,null),
(3,100,110,1110,null,null),
(4,200,220,null,null,null),
(5,200,230,null,null,null),
(5,200,240,null,null,null),
(6,200,240,2410,null,null)
CREATE TABLE [dbo].[accountvaluetotal](
[accountnumber] [int] NULL,
[values] [int] NULL
)
insert into [dbo].[accountvaluetotal]
values
(1110,5000),
(220,7400),
(230,6200),
(2410,5600)
you can use INNER JOIN-
SELECT *
FROM accounthierarchy
INNER JOIN accountvaluetotal
ON level13=accountnumber;
On the basis that the account number is the 'last' value in the accounthierarchy table;
SELECT ID, COALESCE(ac.level5,ac.level4,ac.level3,ac.level2,ac.level1) as AccountNumber
from [accounthierarchy] ac
Will then give you the the account number, to which you can then do a standard join
COALESCE gives the first non-null value from a list of values, so by going from level 5 to level 1, it will return whichever valid value it arrives at first.

SQL Check Constraint with User Defined Function

I am troubleshooting an issue seen in our sql server database recently.
Table 1 has a calculated bit column based off of a user defined function which checks if a record exists in another table and evaluates to 0 if not.
Table 2 is the table searched by the function for Table 1. Table 2 also has a check constraint using a function to see if the bit field in Table 1 is set to 1.
This seems like a circular dependency. The problem is that when inserting a batch of records into Table 2, the constraint fails. Inserting these same records 1 by 1 allows all of the inserts to go through. I can't for the life of me figure out why batch inserts cause it to fail. Any help is appreciated.
CREATE TABLE [dbo].[tPecosPriceCheckStoreSubcategory](
[PecosPriceCheckID] [int] NOT NULL,
[StoreID] [int] NOT NULL,
[SubcategoryID] [int] NOT NULL,
[UndirectedExpectedScans] [int] NULL,
[PriceMin] [decimal](8, 2) NULL,
[PriceMax] [decimal](8, 2) NULL,
[PastPriceVariancePercent] [decimal](5, 2) NULL,
[ChangeDate] [datetime] NOT NULL,
[IsUndirected] [bit] NOT NULL,
[IsDirected] AS ([dbo].[fnPecosPriceCheckStoreSubcategoryIsDirected]([PecosPriceCheckID],[SubcategoryID])),
CONSTRAINT [PK_tPecosPriceCheckStoreSubcategory] PRIMARY KEY CLUSTERED
(
[PecosPriceCheckID] ASC,
[StoreID] ASC,
[SubcategoryID] ASC
)
ALTER TABLE [dbo].[tPecosPriceCheckStoreSubcategory] ADD CONSTRAINT [DF_IsUndirected] DEFAULT ((0)) FOR [IsUndirected]
GO
CREATE FUNCTION [dbo].[fnPecosPriceCheckStoreSubcategoryIsDirected]
(
#PecosPriceCheckID int,
#SubcategoryID int
)
RETURNS bit
AS
BEGIN
declare #isDirected bit
set #isDirected = 0
if (exists (select 1 from tPecosDirectedPriceCheckItem where PecosPriceCheckID = #PecosPriceCheckID and SubcategoryID = #SubcategoryID ))
begin
set #isDirected = 1
end
return #isDirected
END
CREATE TABLE [dbo].[tPecosDirectedPriceCheckItem](
[ID] [int] IDENTITY(1,1) NOT NULL,
[PecosPriceCheckID] [int] NOT NULL,
[PecosItemID] [int] NOT NULL,
[ItemSortOrder] [int] NOT NULL,
[SubcategoryID] [int] NOT NULL,
[ChangeDate] [datetime] NULL,
CONSTRAINT [PK_tPecosDirectedPriceCheckItem] PRIMARY KEY CLUSTERED
(
[ID] ASC
)
ALTER TABLE [dbo].[tPecosDirectedPriceCheckItem] WITH NOCHECK ADD CONSTRAINT [CK_tPecosDirectedPriceCheckItem_CheckPriceCheckSubcategoryDirected] CHECK (([dbo].[fnCheckDirectedItemPriceCheckSubcategory]([PecosPriceCheckID],[SubcategoryID])=(0)))
GO
ALTER TABLE [dbo].[tPecosDirectedPriceCheckItem] CHECK CONSTRAINT [CK_tPecosDirectedPriceCheckItem_CheckPriceCheckSubcategoryDirected]
GO
CREATE FUNCTION [dbo].[fnCheckDirectedItemPriceCheckSubcategory]
(
#PriceCheckID INT,
#SubcategoryID INT
)
RETURNS BIT
AS
BEGIN
DECLARE #isViolation BIT
SET #isViolation =
CASE WHEN EXISTS (SELECT * FROM tPecosPriceCheckStoreSubcategory WHERE SubcategoryID = #SubcategoryID AND PecosPriceCheckID = #PriceCheckID AND IsDirected = 1)
THEN
0
ELSE
1
END
RETURN #isViolation
END

Make column to always contain calculated value without triggers

I was doing practice tests before 70-461 exam and one of questions was to create table that:
Has 4 columns, saleid, unitprice, amount, and price.
Price is calculated by multiplication of unitprice and amount
You can't use triggers.
The last one killed me. How can I do it without triggers? Here is my solutions using instead of triggers.
CREATE TABLE [HR].[Stuff](
[saleid] [int] IDENTITY(1,1) NOT NULL,
[unitprice] [float] NOT NULL,
[amount] [float] NOT NULL,
[price] [float] NULL,
)
GO
CREATE TRIGGER [calculate_price_insert]
ON [HR].[Stuff]
INSTEAD OF INSERT
AS
DECLARE #unitprice float = (SELECT TOP 1 unitprice from inserted )
DECLARE #amount float = (SELECT TOP 1 amount from inserted)
INSERT INTO HR.[Stuff]
VALUES(#unitprice,#amount, #unitprice*#amount)
GO
CREATE TRIGGER [calculate_price_update]
ON [HR].[Stuff]
INSTEAD OF UPDATE
AS
DECLARE #unitprice float = (SELECT TOP 1 unitprice from inserted )
DECLARE #amount float = (SELECT TOP 1 amount from inserted)
UPDATE HR.[Stuff]
SET unitprice = #unitprice, amount = #amount, price = #unitprice*#amount
WHERE unitprice = (SELECT TOP 1 saleid from inserted)
GO
You need to use computed column:
CREATE TABLE [HR].[Stuff](
[saleid] [int] IDENTITY(1,1) NOT NULL,
[unitprice] [float] NOT NULL,
[amount] [float] NOT NULL,
[price] AS ([unitprice] * [amount])
)
LiveDemo
Also storing unitprice and amount as FLOAT may be dangerous because FLOAT isn't accurate datatype. Use DECIMAL(12,4) instead.
Your original trigger solution will fail, because trigger is executed per statement, not per row. Try with:
INSERT INTO [HR].[Stuff](unitprice, amount) VALUES (10, 20), (30, 50), (100,1);
You will loose records with INSERT and get false results with multiple UPDATE.
SqlFiddleDemo

SQL loop executes but new old values are over written

As my question title says, my program loops but all of my values I updated are being overwritten. Here's the code posted below. Say minRownum is 1 and max is 12, I see the loop execute 12 times correctly and min gets updated +1 each time. But in the end result, only the final row in my column whose RowNum is 12 have any values
I'm not exactly sure why overwriting is occurring since I'm saying "Update it where the rownumber = minrownumber" then I increment minrownum.
Can anyone point to what I am doing wrong? Thanks
WHILE (#MinRownum <= #MaxRownum)
BEGIN
print ' here'
UPDATE #usp_sec
set amount=(
SELECT sum(amount) as amount
FROM dbo.coverage
inner join dbo.owner
on coverage.api=owner.api
where RowNum=#MinRownum
);
SET #MinRownum = #MinRownum + 1
END
PS: I edited this line to say (below) and now every value has the same wrong number (its not distinct but duplicated to all.
set amount = (SELECT sum(amount) as amount
FROM dbo.coverage
INNER JOIN dbo.owner ON coverage.api = owner.api
where RowNum=#MinRownum
) WHERE RowNum = #MinRownum;
Tables:
CREATE TABLE dbo. #usp_sec
(
RowNum int,
amount numeric(20,2),
discount numeric(3,2)
)
CREATE TABLE [dbo].[handler](
[recordid] [int] IDENTITY(1,1) NOT NULL,
[covid] [varchar](25) NULL,
[ownerid] [char](10) NULL
)
CREATE TABLE [dbo].[coverage](
[covid] [varchar](25) NULL,
[api] [char](12) NULL,
[owncovid] [numeric](12, 0) NULL,
[amount] [numeric](14, 2) NULL,
[knote] [char](10) NULL
)
CREATE TABLE [dbo].[owner](
[api] [char](12) NOT NULL,
[owncovid] [numeric](12, 0) NULL,
[ownerid] [char](10) NOT NULL,
[officer] [char](20) NOT NULL,
[appldate] [date] NOT NULL
)
Your UPDATE statement needs its own WHERE clause. Otherwise, each UPDATE will update every row in the table.
And the way you have this written, your subquery still needs its WHERE clause too. In fact, you need to definitively correlate the subquery to your table's (#usp_sec) rows. We cannot tell you how that should be done without more information such as your table definitions.