How do I implement a sql command which outputs NULL for salary if it is a voluntary worker?
Here are the tables I created first:
create Table worker (
pid integer references Person,
salary float);
create Table person(
pid integer primary key,
name varchar(30),
adress varchar(30));
Since I'm not sure how to distinguish a normal worker from a voluntary one, I decided to make another table. Unfortunately, I don't know how to insert NULL values for salary for all voluntary workers. That is what I tried out:
create table voluntaryworker(
pid integer references Person,
salary = null);
insert into Person (pid, name, adress) values (1345, anna, 'festreet');
insert into voluntaryworker (pid, salary) values (1345, null);
pid = person ID
Most databases support generated columns. If you really want a salary column in voluntaryworker, then you can use such a column:
create table voluntaryworker (
pid integer references Person,
salary generated always as (cast(null as float))
);
The exact syntax may vary, depending on the database.
Note that having a separate table seems utterly superfluous. Why not just have a flag in the worker table.
Also, representing the salary as a float is quite troublesome. In general, you really should never use floating point representations for monetary amounts. decimal/numeric is much more appropriate for money.
Like others commented, you certainly don't need another table to implement this. All you need is some way to remember whether a worker is voluntary.
To make sure salary sticks to your rule, you can add a CHECK constraint:
CREATE TABLE worker (
pid integer PRIMARY KEY REFERENCES person
, voluntary boolean NOT NULL DEFAULT false
, salary numeric
, CONSTRAINT voluntary_has_no_salary CHECK (NOT voluntary OR salary IS NULL)
);
Meaning: voluntary workers cannot have a nonnull salary.
Alternatively, you might drop the table worker, too, and just add the columns worker_salary and worker_voluntary to table person. (You may need an additional flag worker, or integrate this information in the other two columns ...)
If you are still interested in generated columns (not needed here), see this example with correct syntax and instructions:
Computed / calculated / virtual / derived columns in PostgreSQL
Related:
PostgreSQL: Which Datatype should be used for Currency?
At least insert into voluntaryworker (pid) values (1354); leaves it NULL.
Related
Oracle newbie here. I need to build a database which fulfils the requirements below:
A department is allowed to register for only two programs in a year
The maximum participants in each program must not exceed the number of people in respective departments.
*There are 14 departments in total.
As per requirement, seems like I have to restrict the number of rows inserted.
For example, if the total number of people in Department A is 100, the 101st row has to be rejected.
Apologies if there are many errors as I'm writing this question because now is 1.30AM. I tried to keep the table simple with less columns so it's easier to test the code.
CREATE TABLE department(
DEPT_ID CHAR(5) not null primary key,
TOTAL_P NUMBER);
CREATE TABLE participant(
P_ID CHAR(5) not null primary key,
DEPT_ID CHAR(5) not null);
CREATE TABLE program(
PROG_ID CHAR(5) not null primary key,
PROG_NAME VARCHAR(30),
DEPT_ID CHAR(5),
START_DATE DATE,
END_DATE DATE,
FOREIGN KEY(DEPT_ID) references department(DEPT_ID) on delete cascade);
and I have tried using trigger, but I keep getting warning: trigger created with compilation errors.
(I tried to count the rows in table program and group them by dept_id, then proceed to check the condition)
CREATE OR REPLACE TRIGGER prog
BEFORE INSERT ON program
DECLARE
CountRows NUMBER;
BEGIN
SELECT COUNT(*)
INTO CountRows
GROUP BY DEPT_ID
FROM program;
IF CountRows > 2 THEN
RAISE_APPLICATION_ERROR(-20001, 'Only 2 programs are allowed');
END IF;
END;
/
I don't even know if my idea does make sense or not. I tried many other ways like putting the condition where(to specify dept_id) before begin, after begin, I still get the warning. I have been experimenting a whole day and still cannot figure it out.
MY QUESTIONS:
Is it better to create multiple conditions in one trigger as I will have 14 departments?
if so, how to do that without getting the warning?
any alternative way to restrict the number of rows?
Any help, hints, tips, anything, is deeply appreciated. thanks.
You could do it by maintaining in the trigger handling insert/update/delete operation on the "child" table (e.g. the program), an intersection table "parent_child" (e.g. department_program) containing the 2 foreign keys on the parent/child tables, and an index on which you will put the check constraint (e.g. < 3 for the number of program per department) + any other column defining the scope of the constraint (e.g. here the year of the start_date of the program). The 2 columns with the FK, the index and the other scope columns should be the PK of this intersection table.
e.g.
CREATE TABLE program_department
(
DEPT_ID CHAR(5),
PROG_ID CHAR(5),
PROG_YEAR NUMBER(4),
PROG_IDX NUMBER(10,0) DEFAULT 0,
-- to force always equal to a number the constraint must be defererrable
CONSTRAINT CK_PROG_IDX CHECK (PROG_IDX >= 0 AND PROG_IDX < 3) ENABLE,
PRIMARY KEY (PROG_ID, DEPT_ID, PROG_YEAR, PROG_CNT)
)
;
The idea is to maintain the PROG_IDX that will contain the index of the relation between the department and the program of the specific year.
In the trigger on the table program, you have to update the program_department according to each action, when updating/removing this may imply/implies decrementing the PROG_IDX of the ones having PROG_IDX greater than the one removed.
And of course you will have to apply about the "same" logic for the participant's relationship, however there you can't hardcode the constrain by a CHECK since the # of people in each department is not known at compile time. This case is more complex also because you have to think about the consequence of changes of the # of persons in a department. Probably you will have to keep in the intersection table, the # of people in the department at the start_date of the program.
I have created a table that will create a ID for customers that is a number starting at 101 and increasing by 1 for each customer. So the first customer will have the ID 101, the second will have 102 and so on. In addition to the ID I have other information namely First and Last names. I have also added a constraint that applies to the first and last name columns that will force the entries to be made up by letters.
Here is the SQL statement:
CREATE TABLE tblcustomer
(
CUST_ID INT NOT NULL IDENTITY(101,1) PRIMARY KEY,
FIRST_NAME VARCHAR(15) NOT NULL,
LAST_NAME VARCHAR(15) NOT NULL,
CONSTRAINT firstlet CHECK (FIRST_NAME NOT LIKE '%[^a-zA-Z]%'
AND LAST_NAME NOT LIKE '%[^a-zA-Z]%')
);
This works as intended except for one small issue. When I try to insert say a number for the first name, the constraint will work and not enter anything to the table. But then when I insert the first and last name correctly, it will add the information to the table but the CUST_ID will skip a number.
Example Inserts:
insert into tblcustomer(FIRST_NAME,LAST_NAME) values ('Bob','Smith');
insert into tblcustomer(FIRST_NAME,LAST_NAME) values ('Greg','Johns');
insert into tblcustomer(FIRST_NAME,LAST_NAME) values ('Todd','123');
insert into tblcustomer(FIRST_NAME,LAST_NAME) values ('Todd','Howe');
Output:
CUST_ID FIRST_NAME LAST_NAME
-----------------------------
101 Bob Smith
102 Greg Johns
104 Todd Howe
So where the CUST_ID shows 104 should actually be 103.
Skipping a number is fine. It's normal behavior in any database, and you shouldn't expect the numbers to remain consecutive forever. If this bothers you, try using a GUID key instead.
An identity column value gets updated the moment it receives a request. Hence even when the insertion fails due to validation constraints, the number is already taken.
If your business case requires exact sequence of ID being generated (preserving order of insertion), you will need to set the value of ID column manually using identity_insert as on, then increment the max ID. Do note that if multiple such request come, there can be race conditions where 2 records with same ID are tried to be inserted, and second fails due to primary की constraint.
If all you want with the primary key being unique automatically, use a Guid field. That will save you from all this effort.
Simple example, you are using sequence for an auto increment. With begin transaction inserting a record into the table. But any how you just rollback that transaction.
so next insert will skip that transaction, because it will not hold or place the lock on the sequence.
Sequence will just raise the identity, its job done, If you want to use it or not. and as best practice its good and healthy for performance purpose.
I Have table three tables:
The first one is emps:
create table emps (id number primary key , name nvarchar2(20));
The second one is cars:
create table cars (id number primary key , car_name varchar2(20));
The third one is accounts:
create table accounts (acc_id number primary key, woner_table nvarchar2(20) ,
woner_id number references emps(id) references cars(id));
Now I Have these values for selected tables:
Emps:
ID Name
-------------------
1 Ali
2 Ahmed
Cars:
ID Name
------------------------
107 Camery 2016
108 Ford 2012
I Want to
Insert values in accounts table so its data should be like this:
Accounts:
Acc_no Woner_Table Woner_ID
------------------------------------------
11013 EMPS 1
12010 CARS 107
I tried to perform this SQL statement:
Insert into accounts (acc_id , woner_table , woner_id) values (11013,'EMPS',1);
BUT I get this error:
ERROR at line 1:
ORA-02291: integrity constraint (HR.SYS_C0016548) violated - parent key not found.
This error occurs because the value of woner_id column doesn't exist in cars table.
My work require link tables in this way.
How Can I Solve This Problem Please ?!..
Mean: How can I reference tables in previous way and Insert values without this problem ?..
One-of relationships are tricky in SQL. With your data structure here is one possibility:
create table accounts (
acc_id number primary key,
emp_id number references emps(id),
car_id number references car(id),
id as (coalesce(emp_id, car_id)),
woner_table as (case when emp_id is not null then 'Emps'
when car_id is not null then 'Cars'
end),
constraint chk_accounts_car_emp check (emp_id is null or car_id is null)
);
You can fetch the id in a select. However, for the insert, you need to be explicit:
Insert into accounts (acc_id , emp_id)
values (11013, 1);
Note: Earlier versions of Oracle do not support virtual columns, but you can do almost the same thing using a view.
Your approach should be changed such that your Account table contains two foreign key fields - one for each foreign table. Like this:
create table accounts (acc_id number primary key,
empsId number references emps(id),
carsId number references cars(id));
The easiest, most straightforward method to do this is as STLDeveloper says, add additional FK columns, one for each table. This also bring along with it the benefit of the database being able to enforce Referential Integrity.
BUT, if you choose not to do, then the next option is to use one FK column for the the FK values and a second column to indicate what table the value refers to. This keeps the number of columns small = 2 max, regardless of number of tables with FKs. But, this significantly increases the programming burden for the application logic and/or PL/SQL, SQL. And, of course, you completely lose Database enforcement of RI.
Is there a way in SQL to specify that an attribute is derived? Currently, I'm creating a table Employee, which has a derived attribute age, but I've no idea how to indicate it (and I'm afraid there's no way to do it):
create table Employee(
Id int NOT NULL UNIQUE PRIMARY KEY AUTO_INCREMENT,
Age int, # How to indicate this is a derived attribute?
Country varchar(255),
City varchar(255),
Birthrate double,
);
If what You mean is that age is derived from birthrate, I think You should consider if You really need age column in Your table at all. If You are not creating data warehouse here, calculating age each time will be much saner than maintaining this column in production... Think of it - this column should be recalculated everytime someone have a birthday!
For convenience, You can always create view for it, like this:
CREATE VIEW Employee_view AS
SELECT
e.Id,
=>here some database specyfic calculation to calculate age<= as Age,
e.Country,
e.City,
e.Birthrate
FROM Employee e;
I am trying to create a table with an auto-incrementing primary key in Sqlite3. I am not sure if this is really possible, but I am hoping to only have to designate the other fields.
For example:
CREATE TABLE people (id integer primary key auto increment, first_name varchar(20), last_name varchar(20));
Then, when I add a value, I was hoping to only have to do:
INSERT INTO people
VALUES ("John", "Smith");
Is this even possible?
I am running sqlite3 under cygwin in Windows 7.
You get one for free, called ROWID. This is in every SQLite table whether you ask for it or not.
If you include a column of type INTEGER PRIMARY KEY, that column points at (is an alias for) the automatic ROWID column.
ROWID (by whatever name you call it) is assigned a value whenever you INSERT a row, as you would expect. If you explicitly assign a non-NULL value on INSERT, it will get that specified value instead of the auto-increment. If you explicitly assign a value of NULL on INSERT, it will get the next auto-increment value.
Also, you should try to avoid:
INSERT INTO people VALUES ("John", "Smith");
and use
INSERT INTO people (first_name, last_name) VALUES ("John", "Smith");
instead. The first version is very fragile — if you ever add, move, or delete columns in your table definition the INSERT will either fail or produce incorrect data (with the values in the wrong columns).
Yes, this is possible. According to the SQLite FAQ:
A column declared INTEGER PRIMARY KEY will autoincrement.
As of today — June 2018
Here is what official SQLite documentation has to say on the subject (bold & italic are mine):
The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and
disk I/O overhead and should be avoided if not strictly needed. It is
usually not needed.
In SQLite, a column with type INTEGER PRIMARY KEY is an alias for the
ROWID (except in WITHOUT ROWID tables) which is always a 64-bit signed
integer.
On an INSERT, if the ROWID or INTEGER PRIMARY KEY column is not
explicitly given a value, then it will be filled automatically with an
unused integer, usually one more than the largest ROWID currently in
use. This is true regardless of whether or not the AUTOINCREMENT
keyword is used.
If the AUTOINCREMENT keyword appears after INTEGER PRIMARY KEY, that
changes the automatic ROWID assignment algorithm to prevent the reuse
of ROWIDs over the lifetime of the database. In other words, the
purpose of AUTOINCREMENT is to prevent the reuse of ROWIDs from
previously deleted rows.
SQLite AUTOINCREMENT is a keyword used for auto incrementing a value of a field in the table. We can auto increment a field value by using AUTOINCREMENT keyword when creating a table with specific column name to auto incrementing it.
The keyword AUTOINCREMENT can be used with INTEGER field only.
Syntax:
The basic usage of AUTOINCREMENT keyword is as follows:
CREATE TABLE table_name(
column1 INTEGER AUTOINCREMENT,
column2 datatype,
column3 datatype,
.....
columnN datatype,
);
For Example See Below:
Consider COMPANY table to be created as follows:
sqlite> CREATE TABLE TB_COMPANY_INFO(
ID INTEGER PRIMARY KEY AUTOINCREMENT,
NAME TEXT NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR(50),
SALARY REAL
);
Now, insert following records into table TB_COMPANY_INFO:
INSERT INTO TB_COMPANY_INFO (NAME,AGE,ADDRESS,SALARY)
VALUES ( 'MANOJ KUMAR', 40, 'Meerut,UP,INDIA', 200000.00 );
Now Select the record
SELECT *FROM TB_COMPANY_INFO
ID NAME AGE ADDRESS SALARY
1 Manoj Kumar 40 Meerut,UP,INDIA 200000.00
Have you read this? How do I create an AUTOINCREMENT field.
INSERT INTO people
VALUES (NULL, "John", "Smith");
Always insert NULL as the id.
One should not specify AUTOINCREMENT keyword near PRIMARY KEY.
Example of creating autoincrement primary key and inserting:
$ sqlite3 ex1
CREATE TABLE IF NOT EXISTS room(room_id INTEGER PRIMARY KEY, name VARCHAR(25) NOT NULL, home_id VARCHAR(25) NOT NULL);
INSERT INTO room(name, home_id) VALUES ('test', 'home id test');
INSERT INTO room(name, home_id) VALUES ('test 2', 'home id test 2');
SELECT * FROM room;
will give:
1|test|home id test
2|test 2|home id test 2
Beside rowid, you can define your own auto increment field but it is not recommended. It is always be better solution when we use rowid that is automatically increased.
The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and
disk I/O overhead and should be avoided if not strictly needed. It is
usually not needed.
Read here for detailed information.
What you do is correct, but the correct syntax for 'auto increment' should be without space:
CREATE TABLE people (id integer primary key autoincrement, first_name string, last_name string);
(Please also note that I changed your varchars to strings. That's because SQLite internally transforms a varchar into a string, so why bother?)
then your insert should be, in SQL language as standard as possible:
INSERT INTO people(id, first_name, last_name) VALUES (null, 'john', 'doe');
while it is true that if you omit id it will automatically incremented and assigned, I personally prefer not to rely on automatic mechanisms which could change in the future.
A note on autoincrement: although, as many pointed out, it is not recommended by SQLite people, I do not like the automatic reuse of ids of deleted records if autoincrement is not used. In other words, I like that the id of a deleted record will never, ever appear again.
HTH
I know this answer is a bit late. My purpose for this answer is for everyone's reference should they encounter this type of challenge with SQLite now or in the future and they're having a hard time with it.
Now, looking back at your query, it should be something like this.
CREATE TABLE people (id integer primary key autoincrement, first_name varchar(20), last_name varchar(20));
It works on my end. Like so,
Just in case you are working with SQLite, I suggest for you to check out DB Browser for SQLite. Works on different platforms as well.