Setting Default SQLite3 Fields Dynamically - sqlite

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;

Related

UPDATE not capturing changes to or from NULL

I create a table in which there is an element, parentId, that is linked via a FOREIGN KEY to another uniqueId of the same table:
CREATE TABLE folders (
uniqueId INTEGER PRIMARY KEY,
name TEXT NOT NULL,
parentId INTEGER DEFAULT NULL,
created TEXT DEFAULT (datetime('now', 'localtime')),
updated TEXT DEFAULT (datetime('now', 'localtime')),
FOREIGN KEY (parentId)
REFERENCES folders (uniqueId)
ON DELETE SET DEFAULT
);
I also have a TRIGGER that will set the updated field to the current time upon modifying the parentId field.
CREATE TRIGGER folder_parentId_is_changed
AFTER UPDATE ON folders
WHEN new.parentId <> old.parentId
BEGIN
UPDATE folders
SET updated = datetime('now','localtime')
WHERE parentId = new.parentId;
END;
If the parentId has a non-NULL value, and I change the parentId to another non-NULL value, the TRIGGER works as I desire, and the updated field is set to current time.
However, TRIGGER does not capture changes to or from NULL. That is, if the parentId is currently NULL and I change it so that parentId == 5, the TRIGGER will not update my update field.
Thank you in advance
There are 2 problems with your query.
First, the condition:
WHEN new.parentId <> old.parentId
will not work if either of new.parentId or old.parentId is null, because the result of the comparison with the operator <> will be null and will never be true.
So you must use IS NOT instead of <>, which will work for nulls and not nulls.
Also, the condition in the UPDATE statement:
WHERE parentId = new.parentId
is wrong, because it may return all the rows where parentId is equal to new.parentId, or none if new.parentId is null.
Instead you should set a condition that returns the row that you want to update based on the column uniqueId which is the primary key of the table and will return only the row that is updated:
CREATE TRIGGER folder_parentId_is_changed
AFTER UPDATE ON folders
WHEN NEW.parentId IS NOT OLD.parentId
BEGIN
UPDATE folders
SET updated = DATETIME('now', 'localtime')
WHERE uniqueId = OLD.uniqueId;
END;

setting default value while creating table in SQLite

I am working on SQLite. I want to create a table namely user_role with two column role_id and role_name. And there is an another table namely default that contain role_name and default_val.
I want to set Default value of role_id in the time of creating the table user_role and the Default value have to be retrieved from the table default.
I am new to SQLite and have to idea about the way to doing such recursive query. Please help.
The documentation says:
An explicit DEFAULT clause may specify that the default value is NULL, a string constant, a blob constant, a signed-number, or any constant expression enclosed in parentheses.
You would need to use a subquery, which is not allowed.
However, you could use a trigger that sets the ID if none was specified:
CREATE TRIGGER user_role_id_default
AFTER INSERT ON user_role
FOR EACH ROW
WHEN NEW.role_id IS NULL
BEGIN
UPDATE user_role
SET role_id = (SELECT default_val
FROM "default"
WHERE role_name = NEW.role_name)
WHERE rowid = NEW.rowid;
END;

Will AutoIncrement work with Check constraints?

The question is simple:
In SQLite, if I choose to AutoIncrement a primary key of type NUMERIC which has a check constraint like CHECK(LENGTH(ID) == 10), will it work correctly inserting the first value as 0000000001 and so on?
No, that does not work. Adding a check does not magically also add a way of fullfilling the check to insert the data.
See this SQLFiddle.
If you want to restrict the value of an autoincrement column like that, you need to seed the internal sequence table. (There are other ways.)
create table foo (
foo_id integer primary key autoincrement,
other_columns char(1) default 'x',
check (length(foo_id) = 10 )
);
insert into sqlite_sequence values ('foo', 999999999);
Application code is allowed to modify the sqlite_sequence table, to
add new rows, to delete rows, or to modify existing rows.
Source
insert into foo (other_columns) values ('a');
select * from foo;
1000000000|a
Trying to insert 11 digits makes the CHECK constraint fail.
insert into foo values (12345678901, 'a');
Error: CHECK constraint failed: foo
One alternative is to insert a "fake" row with the first valid id number immediately after creating the table. Then delete it.
create table foo(...);
insert into foo values (1000000000, 'a');
delete from foo;
Now you can insert normally.
insert into foo (other_columns) values ('b');
select * from foo;
1000000001|b
In fact the ID's length is 1, so it doesn't work.

How to autogenerate the username with specific string?

I am using asp.net2008 and MY SQL.
I want to auto-generate the value for the field username with the format as
"SISI001", "SISI002",
etc. in SQL whenever the new record is going to inserted.
How can i do it?
What can be the SQL query ?
Thanks.
Add a column with auto increment integer data type
Then get the maximum value of that column in the table using "Max()" function and assign the value to a integer variable (let the variable be 'x').
After that
string userid = "SISI";
x=x+1;
string count = new string('0',6-x.ToString().length);
userid=userid+count+x.ToString();
Use userid as your username
Hope It Helps. Good Luck.
PLAN A>
You need to keep a table (keys) that contains the last numeric ID generated for various entities. This case the entity is "user". So the table will contain two cols viz. entity varchar(100) and lastid int.
You can then have a function written that will receive the entity name and return the incremented ID. Use this ID concatenated with the string component "SISI" to be passed to MySQL for insertion to the database.
Following is the MySQL Table tblkeys:
CREATE TABLE `tblkeys` (
`entity` varchar(100) NOT NULL,
`lastid` int(11) NOT NULL,
PRIMARY KEY (`entity`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
The MySQL Function:
DELIMITER $$
CREATE FUNCTION `getkey`( ps_entity VARCHAR(100)) RETURNS INT(11)
BEGIN
DECLARE ll_lastid INT;
UPDATE tblkeys SET lastid = lastid+1 WHERE tblkeys.entity = ps_entity;
SELECT tblkeys.lastid INTO ll_lastid FROM tblkeys WHERE tblkeys.entity = ps_entity;
RETURN ll_lastid;
END$$
DELIMITER ;
The sample function call:
SELECT getkey('user')
Sample Insert command:
insert into users(username, password) values ('SISI'+getkey('user'), '$password')
Plan B>
This way the ID will be a bit larger but will not require any extra table. Use the following SQL to get a new unique ID:
SELECT ROUND(NOW() + 0)
You can pass it as part of the insert command and concatenate it with the string component of "SISI".
I am not an asp.net developer but i can help you
You can do something like this...
create a sequence in your mysql database as-
CREATE SEQUENCE "Database_name"."SEQUENCE1" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 001 START WITH 21 CACHE 20 NOORDER NOCYCLE ;
and then while inserting use this query-----
insert into testing (userName) values(concat('SISI', sequence1.nextval))
may it help you in your doubt...
Try this:
CREATE TABLE Users (
IDs int NOT NULL IDENTITY (1, 1),
USERNAME AS 'SISI' + RIGHT('000000000' + CAST(IDs as varchar(10)), 4), --//getting uniqueness of IDs field
Address varchar(150)
)
(not tested)

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