I have a table of milestones in which the primary key is Id_milestone and a table with tasks where foreign key is id_milestone. For each task I attribute completion in percentage. The milestone also have attribute of completion in percentage. I need to update the completion of the milestone at 100 percent until they have completed all the tasks set to 100 percent. I have a DropDownList with interval 10 percent and users update the progress. Sorry for my English.
CREATE TABLE Milestone
(
ID_milestone INTEGER NOT NULL ,
Nazev_milniku VARCHAR2 (30) ,
) ;
CREATE TABLE Milestone_complete
(
ID_milestone INTEGER NOT NULL ,
Completed INTEGER
) ;
CREATE TABLE Tasks
(
ID_tasks INTEGER NOT NULL ,
ID_milestone INTEGER NOT NULL ,
Name_task VARCHAR2 (30) ,
) ;
CREATE TABLE Tasks_complete
(
ID_task INTEGER NOT NULL ,
Completed INTEGER
) ;
This will calculate the percentage of your milestones using all percentages of your tasks.
UPDATE [milestones]
SET [percentage] = (SELECT AVG(pecentage) FROM [tasks] WHERE [tasks].[id_milestone] = [milestones].[id])
If you want to set e.g. a status flag like [completed] you can do something like this:
UPDATE [milestones]
SET [completed] = (CASE WHEN (SELECT AVG(pecentage) FROM [tasks] WHERE [tasks].[id_milestone] = [milestones].[id]) = 100 THEN 1 ELSE 0 END)
This is just a guess what you want as you didn't comppletely show us your tables, structure, etc.
Hope it helps.
EDIT
Your table Milestone need a column percentage or do you want to write the result in the other table Milestone_complete? I don't understand why you have those additional tables.
So we guess you have a column percentage in your Milestone table. In this case the SQL is like this:
UPDATE [Milestone]
SET [percentage] = (SELECT AVG(pecentage)
FROM [Tasks]
WHERE [Tasks].[ID_milestone] = [Milestone].[ID_milestone])
If you want to write the result in the Completed' column in theMilestone_complete` table, then do it like this:
UPDATE [Milestone_complete]
SET [percentage] = (SELECT AVG(pecentage)
FROM [Tasks]
WHERE [Tasks].[ID_milestone] = [Milestone].[ID_milestone])
FROM [Milestone_complete]
JOIN [Milestone] ON [Milestone].[ID_milestone] = [Milestone_Complete].[ID_milestone]
Or do you only want to insert result into Milestone_Complete if all tasks of the milestone is 100%?
I do not know if you already have a record in the compelted table or need to add on in case.
I assume you have one and want to set completed to e.g. 1 (0 = not completed).
UPDATE [Milestone_complete]
SET [completed] = 1
FROM [Milestone_complete]
JOIN [Milestone] ON [Milestone].[ID_milestone] = [Milestone_Complete].[ID_milestone]
WHERE (SELECT AVG(percentage)
FROM [Tasks]
WHERE [Tasks].[ID_milestone] = [Milestone].[ID_milestone])=100
I didn't test anything of it, so not sure if it works, but as I do not 100% your approach you need to modify to your own needs. Hope it helps. Next time you ask a question concentrate on a clear, complete and understandable question, make it easier.
Related
How is it possible to divide 2 columns and update with the result a third one?
UPDATE Table
SET success = (number_won_games / number_all_games)*100
WHERE name_game = 'some name'
This code is not updating my column, so I thought somebody of you guys could help me?
I believe your issue may be that number_won_games and number_all_games are integers and thus that the division will always be be 0 and thus 0 multiplied by 100 will then be 0, perhaps giving the impression that nothing is updated.
You could try casting number_won_games and number_all_games to REAL e.g. using :-
UPDATE Table
SET success = (CAST(number_won_games AS REAL) / CAST(number_all_games AS REAL)) * 100
WHERE name_game = 'some name';
Explanatory Example
Considering the following :-
DROP TABLE IF EXISTS t;
CREATE TABLE IF NOT EXISTS t (name_game TEXT, number_won_games INTEGER, number_all_games INTEGER, success REAL);
INSERT INTO t VALUES
('game1',10,20,0.12345678);
UPDATE t SET success = (number_won_games / number_all_games) * 100;
SELECT * FROM t;
UPDATE t SET success = (CAST(number_won_games AS REAL) / CAST(number_all_games AS REAL)) * 100;
SELECT * FROM t;
WHERE clause not needed, so has not been included.
Which inserts a row with 20 games played with 10 won (thus 50 as the success rate) and with success set to a non-zero value (to show that the first update does update) then
The first UPDATE as per your example result in :-
Whilst the second UPDATE, which casts the values to REAL results in :-
I feel like a trigger is your best bet here. I don't have access to a database at the moment, but something like this would probably work.
CREATE TABLE counter(id INTEGER PRIMARY KEY AUTOINCREMENT, success REAL, number_won_games REAL, number_all_games REAL);
CREATE TRIGGER counter_update_success AFTER UPDATE ON counter
BEGIN
UPDATE counter set NEW.success=((NEW.number_won_games/NEW.number_all_games)*100.0);
END;
CREATE TRIGGER counter_create_success AFTER INSERT ON counter
BEGIN
UPDATE counter set NEW.success=((NEW.number_won_games/NEW.number_all_games)*100.0);
END;
i have a table with 4 columns
1.msisdn
2.accountnumber
3.cardnumber
4.subscriptiondate
I want to add a trigger to this table. If the data i am inserting is
1.99999999
2.2
3.3298572857239
4.(this can be blank)
and the data that is currently in the table is
1.99999999
2.1
3.3298572857239
4.(this can be blank)
Trigger should check if there is this msisdn 99999999 is already having a record with this cardnumber 3298572857239. If there is a record already existing in the table, the trigger should delete the existing entry and insert the new one. The final result should look like this
1.99999999
2.1
3.3298572857239
4.(this can be blank)
I want to keep the value of accountnumber same before and after the trigger. This is what i have tried so far but for this trigger, i am not getting any data in accountnumber column. Please someone help
DROP TRIGGER TRIG_TABLEA;
CREATE OR REPLACE TRIGGER TRIG_TABLEA
BEFORE INSERT ON TABLEA
REFERENCING OLD AS Old NEW AS New
FOR EACH ROW
BEGIN
:new.accountnumber := :old.accountnumber;
DELETE FROM TABLEA WHERE MSISDN = :new.MSISDN AND CARDNUMBER = :new.CARDNUMBER;
:new.MSISDN := :new.MSISDN;
:new.CARDNUMBER := :new.CARDNUMBER;
:new.accountnumber := :old.accountnumber;
END;
/
Don't do a delete-and-insert. You want MERGE. The only thing that can change in your statement is accountnumber and subscriptiondate. You don't say where the data is coming from, so I assume this is a PL/SQL procedure with p_* as the parameters. So you want something like this:
MERGE INTO mytable trg
USING ( SELECT p_accountnumber, p_subscriptiondate FROM dual ) src
ON ( trg.msisdn = p_msisdn AND trg.cardnumber )
WHEN NOT MATCHED INSERT ( msisdn, accountnumber, cardnumber, subscriptiondate )
VALUES ( p_msisdn, p_accountnumber, p_cardnumber, p_subscriptiondate )
WHEN MATCHED SET ( cardnumber = p_cardnumber, subscriptiondate = p_subscriptiondate)
This will do an insert if the row doesn't exist or update an existing row if it does.
This is my table where i want my PNRNo to be generated as 'PNRRES001' for the first entry, and consecutive entries with 'PNRRES002','PNRRES002' so on.
So while creating table only i called that column to function which will generate the PNR no, User just has to enter the CustomerNo from the front end, and data wit PNR & Customer No will updated to the PNRDetails table.
CREATE TABLE PNRDetails(PNRNo AS (DBO.FuncIncPNR()) ,customerNo INT
--FUNCTION TO GENERATE THE PNR NUMBER
ALTER FUNCTION dbo.FuncIncPNR()
RETURNS VARCHAR(20)
AS
BEGIN
DECLARE #RR VARCHAR(20) SET #RR='PNRRESA001'
--here i have checked if no value is there then return the first value as 'PNRRESA001'
IF((SELECT COUNT(*)FROM PNRDetails)=0)
BEGIN
RETURN #RR
END
ELSE
-- if any value is there then take the last value and add 1 to it and update to the table
BEGIN
DECLARE #pnr VARCHAR(20),#S1 VARCHAR(20),#S2 INT
DECLARE PNRCursor CURSOR Static
FOR SELECT PNRNo FROM PNRDetails
OPEN PNRCursor
FETCH LAST FROM PNRNo INTO #pnr
SET #S1=SUBSTRING(#pnr,1,7)
SET #S2=RIGHT(#PNR,3)
SET #S2=#S2+1;
SET #pnr=#S1+#S2;
END
RETURN #pnr
END
--Here am inserting only customerNo as 5 and the PNR should be generated by my function
INSERT INTO PNRDetails VALUES(5)
--it shows 1 row updated :)
SELECT * FROM PNRDetails
-- but when i run select command it shows
--Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32). :(
U can run this.And pls do help if u find anything that could help me. any help will be appreciated...
Waiting for your kind response...
You could try to use a computed column and an identity column instead.
create table PNRDetails
(
ID int identity,
PNRNo as 'PNRRES'+right(1000+ID, 3),
customerNo int
)
I would suggest just using an IDENTITY instead as your id, let SQL Server handle the assignment of each id number with all it's built-in guards for concurrency, and leave the formatting up to the UI....or, create a computed column that defines the formatted version of the ID if you really do need it in the DB.
The risk you run with your intended approach is:
poor performance
concurrency issues - if loats of ids are being generate around the same time
If you are happy to change the table structure. Following will do the job.
CREATE TABLE [dbo].[PNRDetails](
[autoId] [int] IDENTITY(1,1) NOT NULL,
[prnNo] AS ('PNRRES'+right('000'+CONVERT([varchar](3),[dbo].[GetRowCount]([autoId]),(0)),(3))),
[customerNo] [int] NOT NULL,
CONSTRAINT [PK_Table1] PRIMARY KEY CLUSTERED
(
[autoId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
EDIT: to address identity issue for your requirement pls create following function and pass the [autoId] in as above (edited) in the computed column.
CREATE FUNCTION dbo.GetRowCount
(
#autoId INT
)
RETURNS INT
AS
BEGIN
DECLARE #RESULTS AS INT
SELECT #RESULTS = COUNT(autoId) FROM PNRDetails WHERE PNRDetails.autoId<#autoId
RETURN #RESULTS + 1
END
GO
--INSERT
INSERT INTO PNRDetails (customerNo) VALUES(5)
1) You can use an identity column in your database (INTEGER)
PROS: easy/No gaps in between generated ids
CONS: You have to select the inserted id & return via procedure/query
if you were to show it to end user
2) Define a database sequence
PROS: easy to implement/Can be stored/shown to user before the form is
even saved
CONS: Gaps in between if the certain id is once generated & not used
3). Select max(id) from column + 1
PROS: Useful where only single user inserts in a table
CONS: disastrous if you were in an environment where multiple users
were inserting in the same tablle (mismatched max ids)
4) Use a database trigger to autoincrement the column
PROS:automated
CONS: hard to debug (you have to make sure it don't breaks for some
reason otherwise insert fails)
Change the way your trigger works. Something like this
CREATE FUNCTION dbo.fn_FuncIncPNR(#ID int)
RETURNS varchar(20)
BEGIN
Declare #Retval varchar(20),
#No varchar(4)
Select #No = convert(varchar(4), #ID)
while Len(#No) < 4
Select #No = '0' + #No
Select #Retval = 'PNRRESA' + #No
RETURN #Retval
END
You will notice there is a parameter field
Change your table create to this
CREATE TABLE PNRDetails(PNRNo AS (dbo.fn_ShowPNRNo(wID)), wID int IDENTITY(1,1) NOT NULL, customerNo INT)
That should solve your problem
I have a stored procedure which uses the IN clause. In my ASP.NET application, I have a multiline textbox that supplies values to the stored procedure. I want to be able to order by the values as they were entered in the textbox. I found out how to do this easily in mySQL (using FIELD function), but not a SQL Server equivalent.
So my query looks like:
Select * from myTable where item in #item
So I would be passing in values from my application like '113113','112112','114114' (in an arbitrary order). I want to order the results by that list.
Would a CASE statement be feasible? I wouldn't know how many items are coming in the textbox data.
How are you parameterising the IN clause?
As you are on SQL Server 2008 I would pass in a Table Valued Parameter with two columns item and sort_order and join on that instead. Then you can just add an ORDER BY sort_order onto the end.
From KM's comment above...
I know you didn't state it is comma seperated, but if it was a CSV or even if you have it space seperated you could do the following.
DECLARE #SomeTest varchar(100) --used to hold your values
SET #SomeTest = (SELECT '68,72,103') --just some test data
SELECT
LoginID --change to your column names
FROM
Login --change to your source table name
INNER JOIN
( SELECT
*
FROM fn_IntegerInList(#SomeTest)
) n
ON
n.InListID = Login.LoginID
ORDER BY
n.SortOrder
And then create fn_IntegerInList():
CREATE FUNCTION [dbo].[fn_IntegerInList] (#InListString ntext)
RETURNS #tblINList TABLE (InListID int, SortOrder int)
AS
BEGIN
declare #length int
declare #startpos int
declare #ctr int
declare #val nvarchar(50)
declare #subs nvarchar(50)
declare #sort int
set #sort=1
set #startpos = 1
set #ctr = 1
select #length = datalength(#InListString)
while (#ctr <= #length)
begin
select #val = substring(#InListString,#ctr,1)
if #val = N','
begin
select #subs = substring(#InListString,#startpos,#ctr-#startpos)
insert into #tblINList values (#subs, #sort)
set #startpos = #ctr+1
end
if #ctr = #length
begin
select #subs = substring(#InListString,#startpos,#ctr-#startpos)
insert into #tblINList values (#subs, #sort)
end
set #ctr = #ctr +1
set #sort = #sort + 1
end
RETURN
END
This way your function creates a table that holds a sort order namely, SortOrder and the ID or number you are passing in. You can of course modify this so that you are looking for space rather then , values. Otherwise Martin has the right idea in his answer. Please note in my example I am using one of my tables, so you will need to change the name Login to whatever you are dealing with.
the same way you concatenate ('113113','112112','114114') to pass to the sql sentence in the where clausule you can concatenate
order by
case item
when '113113' then 1
when '112112' then 2
when '114114' then 3
end
to pass to your order by clausule
I have a SQLite table with a Balance field that will be used as a running total. I have a trigger defined that calculates the Balance on insert of new records, but it doesn't fire on the first insert, so the Balance in the first record is zero. All following records calculate correctly.
Current trigger definition:
CREATE TRIGGER [UpdateBalance_Insert] AFTER INSERT ON [Transaction] FOR EACH ROW BEGIN
REPLACE INTO [Transaction]
SELECT t1.[ID],
t1.[Date],
t1.[Transaction],
t1.[Debit],
t1.[Credit],
( SELECT SUM( t2.[Credit] ) - SUM( t2.[Debit] ) + new.[Credit] - new.[Debit]
FROM [Transaction] AS t2
WHERE t2.[Date] < t1.[Date] OR (
t2.[Date] = t1.[Date] AND t2.[ID] < t1.[ID] )
) AS [Balance]
FROM [Transaction] AS t1
WHERE [ID] = new.[ID];
UPDATE [Transaction]
SET [Balance] = [Balance] + new.[Credit] - new.[Debit]
WHERE [Date] > new.[Date];
END
Edit: The nature of my question changed between typing the title and typing the description. Above is my original trigger. My original question was whether a separate trigger could be created to handle the first insert only, but now I wonder if the existing trigger could be modified to perform the needed functionality.
I'd try to check if the table is empty first, and if it is, hard-code a balance. If not, i'd compute it using your algorithm.