Integer dividing columns - SQLite - sqlite

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;

Related

Converting a field to lower case and merging data in an sqlite database

I need to merge some randomly uppercased data that has been collected in an SQLite table key_val, such that key is always lowercase and no vals are lost. There is a unique compound index on key,val.
The initial data looks like this:
key|val
abc|1
abc|5
aBc|1
aBc|5
aBc|3
aBc|2
AbC|1
abC|3
The result after the merge would be
key|val
abc|1
abc|2
abc|3
abc|5
In my programmer brain, I would
for each `key` with upper case letters;
if a lower cased `key` is found with the same value
then delete `key`
else update `key` to lower case
Re implementing the loop has a sub query for each row found with upper case letters, to check if the val already exists as a lower case key
If it does, I can delete the cased key.
From there I can UPDATE key = lower(key) as the "duplicates" have been removed.
The first cut of the programming method of finding the dupes is:
SELECT * FROM key_val as parent
WHERE parent.key != lower(parent.key)
AND 0 < (
SELECT count(s.val) FROM key_val as s
WHERE s.key = lower(parent.key) AND s.val = parent.val
)
ORDER BY parent.key DESC;
I'm assuming there's a better way to do this in SQLite? The ON CONFLICT functionality seems to me like it should be able to handle the dupe deletion on UPDATE but I'm not seeing it.
First delete all the duplicates:
DELETE FROM key_val AS k1
WHERE EXISTS (
SELECT 1
FROM key_val AS k2
WHERE LOWER(k2.key) = LOWER(k1.key) AND k2.val = k1.val AND k2.rowid < k1.rowid
);
by keeping only 1 combination of key and val with the min rowid.
It is not important if you kept the key with all lower chars or not, because the 2nd step is to update the table:
UPDATE key_val
SET key = LOWER(key);
See the demo.
Honestly it might just be easier to create a new table and then insert into it. As it seems you really just want a distinct select here, use:
INSERT INTO kev_val_new ("key", val)
SELECT DISTINCT LOWER("key"), val
FROM key_val;
Once you have populated the new table, you may drop the old one, and then rename the new one to the previous name:
DROP TABLE key_val;
ALTER TABLE key_val_new RENAME TO key_val;
I agree with #Tim that it would be easire to re-create table using simple select distict lower().. statement, but that's not always easy if table has dependant objects (indexes, triggers, views). In this case this can be done as sequence of two steps:
insert lowered keys which are not still there:
insert into t
select distinct lower(tr.key) as key, tr.val
from t as tr
left join t as ts on ts.key = lower(tr.key) and ts.val = tr.val
where ts.key is null;
now when we have all lowered keys - remove other keys:
delete from t where key <> lower(key);
See fiddle: http://sqlfiddle.com/#!5/84db50/11
However this method assumes that key is always populated (otherwise it would be a strange key)
If vals can be null then "ts.val = tr.val" should be replaced with more complex stuff like ifnull(ts.val, -1) = ifnull(tr.val, -1) where -1 is some unused value (can be different). If we can't assume any unused value like -1 then it should be more complex check for null / not null cases.

How to combine an insert statement and an update statement in sqlite3?

I have a table (try), having a primary key (pk), several data fields (dx, dy, dz), and a time field (td). In sqlite3, I would like to:
Insert a record(pk, dx, dy, dz) if it (pk) does not exist.
If record exists, update it.
During update, If there is any change to some of the fields (dx,dy) update td to current time.
During update, If there was no change in above fields (dx,dy) leave td as is (original time).
REPLACE comes close to what I need, but it does an "insert" or "replace all", while I need an "insert" or "update a few".
Here is something that works, but this needs that 2 statements
One to check if record exists first, then next to do insert or update. How to perform these in a single statement?
-- Example table
CREATE TABLE try(
pk INTEGER PRIMARY KEY,
dx TEXT NOT NULL, -- update td if this changes.
dy TEXT, -- update td if this changes.
dz TEXT, -- no need to change td, if this changes.
td DATETIME DEFAULT (strftime('%s','now'))
);
-- 1. Check if record exists or not.
SELECT pk from try where pk=1;
-- 2.a If record does not exist, do this insert.
INSERT INTO try(pk, dx, dy, td) VALUES(1, 'data_x', 'data_y', 'data_z', strftime('%s', 'now'));
-- 2.b If record exists, update but be selective with td.
UPDATE try SET dx='data_x',
dy='data_y_new',
dz='data_z_new',
td=(CASE WHEN (try.dx IS NOT 'data_x' OR try.dy IS NOT 'data_y_new') THEN strftime('%s','now')
ELSE try.td
END)
WHERE pk=1;
I was able to combine the statements for my requirement, and here it goes just for reference, for someone else who might need it later. However, note that working with multiple statements within a transaction (as suggested by CL) might be simpler, in general..
REPLACE INTO try VALUES(1,
'data_x',
'data_y_new',
'data_z_new',
CASE WHEN EXISTS(SELECT td FROM try AS try2 WHERE (try2.pk IS 1 AND try2.dx IS 'data_x' AND try2.dy IS 'data_y_new'))
THEN (SELECT td FROM try AS try3 WHERE (try3.pk IS 1))
ELSE strftime('%s','now')
END);

Setting Default SQLite3 Fields Dynamically

I have an SQLite3 database that I would like to create. I want an INTEGER field (named "Length") to have a DEFAULT value that equals the length of the string in another field (named "Pattern").
CREATE TABLE knowledge (
Entry INTEGER PRIMARY KEY AUTOINCREMENT,
Priority TINYINT UNSIGNED CHECK (0 <= Priority < 15),
Pattern TEXT NOT NULL,
Length INTEGER UNSIGNED DEFAULT 'LENGTH(Pattern);'
);
However, the current table set-up does not "dynamically" set the value of "Length" as desired.
How can I properly set the DEFAULT value of "Length" to be the string length of the "Pattern" field?
The Default value you want to assign is dynamic which sqlite does not support. One solution is what CL. said. I would define the default value as 0 and use not one but two triggers (one for insert and another for update).
CREATE TRIGGER default_length_on_insert AFTER INSERT ON knowledge WHEN NEW.Length IS 0
BEGIN
UPDATE knowledge SET Length=length(NEW.Pattern) WHERE ROWID = NEW.ROWID;
END;
and
CREATE TRIGGER default_length_on_update AFTER UPDATE ON knowledge
BEGIN
UPDATE knowledge SET Length=length(NEW.Pattern) WHERE ROWID = NEW.ROWID;
END;
A default value must be a constant.
You coud use a trigger instead:
CREATE TRIGGER knowledge_length_default
AFTER INSERT ON knowledge
FOR EACH ROW
WHEN NEW.Length IS NULL
BEGIN
UPDATE knowledge
SET Length = length(NEW.Pattern)
WHERE Entry = NEW.Entry;
END;

SQL update attribute

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.

How to generate a column with id which increments on every insert

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

Resources