MariaDB Check value of an attribute w/ another table attribute - mariadb

I want to assure at inserting a manager that department manager start date [DEPARTMENT.mgr_start_date] is coming after his birthdate [EMPLOYEE.bdate],
how can I do that?
CREATE TABLE IF NOT EXISTS EMPLOYEE
(
ssn INT(16) unsigned NOT NULL,
fname VARCHAR(16),
lname VARCHAR(16),
bdate DATE,
address VARCHAR(32),
gender enum('m','f'),
salary decimal(16,2),
Dno VARCHAR(8),
PRIMARY KEY (ssn)
);
CREATE TABLE IF NOT EXISTS DEPARTMENT
(
mgr_ssn INT(16) unsigned,
Dname VARCHAR(32),
mgr_start_date DATE,
Dnumber VARCHAR(8),
PRIMARY KEY (Dnumber),
FOREIGN KEY (mgr_ssn) REFERENCES EMPLOYEE(ssn)
);

You would have to do this with a trigger.
CHECK constraints can reference only columns in the table where the constraint is defined.
The full SQL standard includes a type of constraint called an ASSERTION, which allows multi-table constraints, but MariaDB does not implement this feature of SQL (very few brands of SQL databases do implement it).
CREATE TRIGGER t BEFORE INSERT ON DEPARTMENT
FOR EACH ROW BEGIN
IF NEW.mgr_start_date < (SELECT bdate FROM EMPLOYEE WHERE ssn = NEW.mgr_ssn) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'manager is way too young';
END IF;
END
Test:
insert into EMPLOYEE set ssn=123, bdate='2021-01-01';
insert into DEPARTMENT set mgr_ssn=123, dnumber='1', mgr_start_date='2010-01-01';
ERROR 1644 (45000): manager is way too young

Related

Trigger is not created in sqlite

I'm building a database for a library management system, so I have created three tables: books, members and borrow(describe the relation between the two tables).
for each book I store in the books table, I store it's quantity.
for each member that want to borrow a book, I store his id and the book id in the borrow table.
for every time a member want to borrow a book, I want to check that borrowed quantity is equal or less than the quantity stored for that book in the books table(in case it was more it will rise an error and will not accept the new data) , so I tried to achieve this using a trigger
the problem is that when I try to run the trigger it's not created, and it does not even give an error message, when I try even to see the trigger that is created in the database using the command:(select name from sqlite_master where type = 'trigger';) it does not show any thing
here is the code:
CREATE TABLE books(
book_id INTEGER CHECK (book_id>0999) PRIMARY KEY AUTOINCREMENT,
book_title VARCHAR(20) NOT NULL,
author_name VARCHAR(20),
quantity INT NOT NULL,
genre VARCHAR(20) NOT NULL,
book_place VARCHAR(20) NOT NULL,
UNIQUE(book_title,author_name)
);
CREATE TABLE members(
member_id INTEGER PRIMARY KEY AUTOINCREMENT CHECK(member_id<1000) ,
member_name VARCHAR(20) NOT NULL,
member_phone TEXT NOT NULL UNIQUE
CHECK (LENGTH(member_phone)==11 AND member_phone NOT GLOB '*[^0-9]*'
AND (SUBSTR(member_phone,1,3)=='010' OR SUBSTR(member_phone,1,3)=='011'
OR SUBSTR(member_phone,1,3)=='012' OR SUBSTR(member_phone,1,3)=='015' )),
sub_startDate TEXT NOT NULL CHECK(sub_startDate IS DATE(sub_startDate)),
sub_endDate TEXT NOT NULL CHECK(sub_endDate IS DATE(sub_endDate))
);
CREATE TABLE borrow(
member_id INTEGER NOT NULL,
book_id INTEGER NOT NULL,
borrowed_date TEXT NOT NULL CHECK(borrowed_date IS DATE(borrowed_date)),
return_date TEXT NOT NULL CHECK (return_date IS DATE(return_date)),
FOREIGN KEY(member_id) REFERENCES members(member_id) ON DELETE CASCADE ,
FOREIGN KEY(book_id) REFERENCES books(book_id) ON DELETE CASCADE,
PRIMARY KEY(member_id,book_id)
);
CREATE TRIGGER not_enough_copies
BEFORE INSERT, UPDATE
ON borrow
WHEN
(SELECT((SELECT
COUNT(*)
FROM borrow
WHERE book_id=NEW.book_id)
NOT BETWEEN 1 AND
(SELECT quantity FROM books WHERE books.book_id==NEW.book_id)))
BEGIN
RAISE(ABORT,'ERROR!..This book is not available in the library right now')
END;

Unique constraint violation in PostgreSQL Trigger

I have below two tables. I wrote after insert trigger on employees table. If i insert the record in employees table it will insert the record in employee_audits table.
Both table have primary key column (id). suppose if you try to insert record ID value which does not exists in employees table and exists in employee_audits table, it shows the
error duplicate key value violates unique constraint "employee_audits_pkey" and also it is not inserting record in employees table. Both transaction failed.
But i want to insert the record in employees table.
CREATE TABLE employees(
id SERIAL PRIMARY KEY,
first_name VARCHAR(40) NOT NULL,
last_name VARCHAR(40) NOT NULL
);
CREATE TABLE employee_audits (
id SERIAL PRIMARY KEY,
last_name VARCHAR(40) NOT NULL,
changed_on TIMESTAMP(6) NOT NULL
)
The trigger function:
CREATE OR REPLACE FUNCTION log_last_name_changes()
RETURNS trigger AS
$BODY$
BEGIN
INSERT INTO employee_audits(last_name,changed_on)
VALUES(NEW.last_name,now());
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
The trigger definition:
CREATE TRIGGER last_name_changes
AFTER INSERT
ON employees
FOR EACH ROW
EXECUTE PROCEDURE log_last_name_changes();
INSERT INTO employees (first_name, last_name)
VALUES ('John', 'Doe');
SELECT * FROM EMPLOYEES
id first_name last_name
1 "John" "Doe"
SELECT * FROM EMPLOYEE_AUDITS
ID last_name CHANGED_ON
1 "Doe" "2019-12-27 17:21:13.934"
Manual insert on second table
insert into employee_audits values(2,'banu','2019-12-27 17:21:13.934')
Manual insert on first table
INSERT INTO employees (first_name, last_name)
VALUES ('David', 'Raj');
Error duplicate key value violates unique constraint "employee_audits_pkey"
is it possible to insert record in employees table?
If you have a serial column you should never provide the value for it manually. Manually providing a value for a serial will not advance the sequence behind that column, so the next time you insert without specifying the id column, the next sequence value will be taken which is 2 as the sequence was only advanced once.
So instead of:
insert into employee_audits values(2,'banu','2019-12-27 17:21:13.934')
Just use:
insert into employee_audits (last_name, changed_at)
values ('banu','2019-12-27 17:21:13.934');
This behaviour of the serial columns is one of the reasons why it's highly recommended to use identity columns with modern Postgres versions.

Sqlite INSERTION optimization

Good day SO,
I'm working on a program in PowerShell to manipulate an SQLite DB I created. I've never written a serious applications to utilize a DB so right now I'm super interested in optimizing my querys, so I'm really interested in feed back. My primary issue is I have a lot of data that I want to include in a separate table that may or not exist already. All my research really seemed to lead to perform an INSERT and let the UNIQUE constraints sort it out, than do a select on the new record which seemed like two table scans and inefficient. So my solution was Create a temp table, insert into the temp table FROM the table with data I want and perform an INSERT if the data was not in the temporary table. I'm a few drinks in tonight and haven't tested the code so please don't critique small typos, I just want to know if my methodology is out to lunch, and if so please provide better direction.
My table is as shown:
CREATE TABLE Processes (
pk INTEGER PRIMARY KEY AUTOINCREMENT
UNIQUE,
hostname INTEGER NOT NULL,
artifacttype INTEGER REFERENCES ArtifactType (pk),
processname INTEGER REFERENCES ProcessesName (pk),
filelocation INTEGER NOT NULL
REFERENCES files (pk),
pid INTEGER,
ppid INTEGER,
starttime INTEGER,
stoptime INTEGER,
token STRING,
logonid INTEGER,
exitstatus INTEGER,
threadcount INTEGER,
commandline INTEGER REFERENCES ProcessesCommandline (pk),
user INTEGER REFERENCES users (pk),
PeakVirtualSize INTEGER,
VirtualSize INTEGER,
PeakWorkingSetSize INTEGER,
suspicious BOOLEAN,
malicious BOOLEAN
);
Transaction:
#"
CREATE TEMPORARY TABLE IF NOT EXISTS Results(pk INTEGER, data TEXT);
INSERT INTO Results(pk, data) VALUES ((SELECT pk, name FROM ProcessesName WHERE name = #processname));
INSERT INTO ProcessesName(name) VALUES (SELECT #processname WHERE NOT EXISTS (SELECT pk FROM Results WHERE data = #processname));
INSERT INTO Results (pk, data) VALUES ((SELECT last_insert_row_id, #processname WHERE NOT EXISTS (SELECT pk FROM Results WHERE data =#processname)));
INSERT INTO Results(pk, data) VALUES ((SELECT pk, file FROM Files WHERE file = #filelocation));
INSERT INTO Files(file) VALUES (SELECT #filelocation WHERE NOT EXISTS (SELECT pk FROM Results WHERE data = #filelocation));
INSERT INTO Results (pk, data) VALUES ((SELECT last_insert_row_id, #filelocation WHERE NOT EXISTS (SELECT pk FROM Results WHERE data =#filelocation)));
INSERT INTO Results(pk, data) VALUES ((SELECT pk, commandline FROM ProcessesCommandline WHERE commandline = #commandline));
INSERT INTO ProcesseCommandline(commandline) VALUES (SELECT #commandline WHERE NOT EXISTS (SELECT pk FROM Results WHERE data = #commandline));
INSERT INTO Results (pk, data) VALUES ((SELECT last_insert_row_id, #filelocation WHERE NOT EXISTS (SELECT pk FROM Results WHERE data =#commandline)));
INSERT INTO Results(pk, data) VALUES ((SELECT pk, SID FROM Users WHERE SID = #SID));
INSERT INTO Users(SID) VALUES (SELECT #SID WHERE NOT EXISTS (SELECT pk FROM Results WHERE data = #filelocation));
INSERT INTO Results (pk, data) VALUES ((SELECT last_insert_row_id, #SID WHERE NOT EXISTS (SELECT pk FROM Results WHERE data =#SID)));
INSERT INTO processes(hostname, artifacttype, processname, filelocation, pid, ppid, starttime, threadcount, commandline, user, PeakVirtualSize, VirtualSize, PeakWorkingSetSize)
VALUES (#hostname, #artifacttype, (SELECT pk FROM Results WHERE data = #processname), (SELECT pk FROM Results WHERE data #filelocation), #pid, #ppid, #starttime, #threadcount, (SELECT pk FROM Results where data = #commandline), SELECT pk FROM Results WHERE data = #SID, #PeakVirtualSize, #VirtualSize, #PeakWorkingSetSize);
DROP TABLE Results;
"#
*there are a few foreign keys where the data is being tracked application side so no complex queries are required.
So my core question is, is there a more efficient way to do this?
Thanks guys!

SQLite Multi-column Insert/Replace with Multiple Join

Sorry for the poor title. I have a query (below) that executes properly and creates an insertion just as I would desire. However, I want to make it smarter by only inserting when the exact combination of three columns. Essentially, the three column tuple is a primary key, but I'm working with the limitation of sqlite's single primary key.
Basic Context
I have 4 tables: Permissions, Roles, Users, Actions
Permissions connects Roles and Users to Actions. The Actions table has a list of available tasks that a User or a user with a Role can perform. So for example, if user_id = 1 can perform a list_folder action (action_id = 1), then the permissions table would have an entry: (id=1, action_id=1, user_id=1, role_id=NULL). Likewise, suppose an owner_role (role_id=1) might have permissions to perform a list_folder action (action_id=1), then the permissions entry might be: (id=2, action_id=1, user_id=NULL, role_id=1).
When I do an insert, I want to make sure that I do not already have that exact combination (e.g. action_id=1, user_id=NULL, role_id=1). And I'm not entirely sure how to write the sql so that I have this setup properly.
Here's my basic insert statement. I need to come up with an insert and a replace statement:
INSERT INTO permissions (
action_id
,role_id
)
SELECT DISTINCT
a.id as "action_id"
,r.id as "role_id"
FROM tmp_permissions tmp
LEFT OUTER JOIN actions a
ON tmp.action_name = a.name
LEFT OUTER JOIN roles r
ON tmp.roles_name = r.name
LEFT OUTER JOIN permissions p
ON p.role_id
Here are some creation sql statements for the tables:
CREATE TABLE permissions (
id INTEGER NOT NULL,
enabled INTEGER,
action_id INTEGER,
user_id INTEGER,
role_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(user_id) REFERENCES users (id),
FOREIGN KEY(action_id) REFERENCES actions (id),
FOREIGN KEY(role_id) REFERENCES roles (id)
);
CREATE TABLE actions (
id INTEGER NOT NULL,
enabled INTEGER,
name VARCHAR(50),
permission_ids INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(permission_ids) REFERENCES permissions (id)
);
CREATE TABLE roles (
id INTEGER NOT NULL,
enabled INTEGER,
name VARCHAR(50),
permission_ids INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(permission_ids) REFERENCES permissions (id)
);
CREATE TABLE users (
id INTEGER NOT NULL,
enabled INTEGER,
name VARCHAR(50),
permission_ids INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(permission_ids) REFERENCES permissions (id)
);
Here's a temp table I'm using to store the data in the table while I work with it:
CREATE TABLE tmp_permissions(
roles_name VARCHAR(50),
action_name VARCHAR(50)
);
Here's some data:
#role|action
admin|setup
admin|debug
admin|login
admin|view_user
manager|view_employee
manager|enroll_employee
manager|login
employee|schedule
employee|login
customer|guest_login
customer|change_credentials
guest|guest_login
Thanks in advance!
Add a UNIQUE constraint to the table:
CREATE TABLE permissions(
... ,
UNIQUE (action_id, user_id, role_id)
)
You can then use any of the conflict resolution algorithms to handle duplicates.

shopping cart database design and the flow of putting the orders into the tables

I'm creating a database in SQL Server 2005 to store orders taken.
table [customers] : customer detail table primary key is the customer_ID which will be an identity autoincremental
table [orders] : holds 3 columns, [orderid](which is also the pk),[product_id],[quantity]
table [linking] : holds 2 columns, [customerid](as foreign key), [orderid](foreign key as well)
ordering flow :
when customer checked out, the customer's detail will be stored to table[customers] in which a unique customer_ID will be generated.
next, using that customer_ID, the products in the shopping cart will be stored into table[orders] .
now, the problem is: how do i retrieve the auto_generated customer_ID from the table[customers]? assuming that a lot of users are checking out at the same time? After inserting the customer's detail into the table[customer], I have to use the customer_ID in the table[linking] to pair up with the orderid.
Write a few SQL stored procedures to do this work for you. You can call this from your web application's code using ADO.NET.
Call proc CreateCustomer. It creates your CustomerID.
Call proc CreateOrderForCust.
CREATE PROC CreateCustomer
#Name varchar(100),
#Address varchar(100)
AS
DECLARE #CustomerID int;
INSERT INTO CUSTOMER([Name],[Addr]) VALUES (#Name, #Addr);
SELECT #CustomerID = SCOPE_IDENTITY();
RETURN #CustomerID;
...
CREATE PROC CreateOrderForCust
#CustomerID int,
#SKU int,
#Qty int
AS
.....

Resources