MySQL 1:10 fixed relationship. How to implement it at database level? - mysql

Is it possible to implement a 1:N relationship that has 10 rows of the many referencing 1 row from the one table at most?
// ID int
INSERT INTO one VALUES (1);
// ...
INSERT INTO one VALUES (25);
//ID int, one_id int
INSERT INTO many VALUES (1,1);
// ...
INSERT INTO many VALUES (1,10);
INSERT INTO many VALUES (1,11); // ERROR!
INSERT INTO many VALUES (2,11); // working
// ...
INSERT INTO many VALUES (2,20); // working
INSERT INTO many VALUES (2,21); // ERROR!

You can do this with a trigger:
create trigger trg_mytable_max10 before insert on mytable
for each row
begin
declare cnt int;
set cnt = (select count(*) from mytable where col1 = new.col1);
if cnt = 10 then
signal sqlstate '45000' set message_text = 'only 10 records per col1 allowed';
end if;
end;
http://rextester.com/EORH56497

Related

Disable manually setting auto-incremented ids in MySQL

I want to disallow users from inserting into a table product (which has auto-incremented ids) if they're setting the id manually.
So this would be valid (id generated automatically since it's set as AUTO_INCREMENT):
INSERT INTO product (name) VALUES ("Product1")
But this wouldn't (id being set manually):
INSERT INTO product (id, name) VALUES (10, "Product1")
Is this possible in any way?
Trigger logic may help.
SET SESSION sql_mode := '';
CREATE TABLE test (
id INT AUTO_INCREMENT PRIMARY KEY,
val INT
) AUTO_INCREMENT = 123;
CREATE TRIGGER fail_explicit_id
BEFORE INSERT ON test
FOR EACH ROW
BEGIN
IF NEW.id <> 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Explicit ''id'' value is not allowed.';
END IF;
END
INSERT INTO test (val) VALUES (111);
INSERT INTO test VALUES (NULL, 222);
INSERT INTO test VALUES (0, 333);
INSERT INTO test VALUES (DEFAULT, 444);
INSERT INTO test VALUES (456, 555);
Explicit 'id' value is not allowed.
SET SESSION sql_mode := CONCAT_WS(',', ##sql_mode, 'NO_AUTO_VALUE_ON_ZERO');
SELECT ##sql_mode;
INSERT INTO test VALUES (0, 666);
INSERT INTO test VALUES (0, 777);
Duplicate entry '0' for key 'test.PRIMARY'
SELECT * FROM test;
id
val
0
666
123
111
124
222
125
333
126
444
fiddle
Give the user column-level permissions.
grant insert(`name`) on yourdatabase.product to theuser#thehost;
Then the user tries these:
mysql> INSERT INTO product (name) VALUES ("Product1");
Query OK, 1 row affected (0.01 sec)
mysql> INSERT INTO product (id, name) VALUES (10, "Product1");
ERROR 1143 (42000): INSERT command denied to user 'theuser'#'thehost' for column 'id' in table 'product'

Update column value of another table in After Insert Trigger

I have two different tables: OrderItem and Item. Both tables have a column named "pieces". In the case of table OrderItem the pieces are the amount of each item in the OrderItem's list and in the table Item the pieces represent the remaining pieces of a product (in a warehouse for example).
So I am building an after insert trigger that will update the pieces in Items after an insert is done in OderItem
delimiter //
create trigger TrgItemPieces
after insert on OrderItem
for each row
begin
declare PiecesNew int;
select a.pieces - new.Pieces into PiecesNew from Item a where a.ID = new.ItemId;
update Item set pieces = PiecesNew where id = new.ItemId;
end//
delimiter ;
But when I do an insert
INSERT INTO OrderItem VALUES ([OrderID],[ItemID],Pieces); I get the following error:Error Code: 1442. Can't update table 'Item' in stored function/trigger because it is already used by statement which invoked this stored function/trigger..
What am I doing wrong and how to fix it?
EDIT:
I get the ItemID by doing a select in the insert statement INSERT INTO ORDERITEM VALUES(1,(select i.ID from Item where i.Name = 'Item1'),5);
It's a safety measure from MySql to avoid deadlocks.
But you can avoid it by not directly selecting from the table that will be updated via the trigger.
For example by using variables.
CREATE TRIGGER TrgOrderItemAftIns
AFTER INSERT ON ORDERITEM
FOR EACH ROW
BEGIN
INSERT INTO DEBUG_TABLE (MSG) VALUES (CONCAT('TrgOrderItemAftIns: ', NEW.ITEMID,',',NEW.PIECES));
UPDATE ITEM SET PIECES = PIECES - NEW.PIECES WHERE ID = NEW.ITEMID;
END;
SET #OrderID = (select ID from TabOrder where Name = 'Order 1');
SET #ItemID = (select ID from ITEM where Name = 'Item1');
INSERT INTO ORDERITEM (OrderID, ITEMID, PIECES) VALUES
(#OrderID, #ItemID, 5);
SET #OrderID = (select ID from TabOrder where Name = 'Order 1');
SET #ItemID = (select ID from ITEM where Name = 'Item2');
INSERT INTO ORDERITEM (OrderID, ITEMID, PIECES) VALUES
(#OrderID, #ItemID, 1);
SET #OrderID = (select ID from TabOrder where Name = 'Order 2');
SET #ItemID = (select ID from ITEM where Name = 'Item1');
INSERT INTO ORDERITEM (OrderID, ITEMID, PIECES) VALUES
(#OrderID, #ItemID, 3);
Demo on db<>fiddle here

How to add data in history table with triggers only when it contains changes

There is a python script that updates data in MySQL table. The problem is that when no changes has been made in data, trigger makes duplicate of every record in history_table.
create trigger organizations_history_update after update on organizations for each row
begin
declare N DATE;
set N = now();
UPDATE organizations_history
SET EndDate = N
WHERE id = OLD.id
AND EndDate IS NULL;
insert into organizations_history (id, name,current_version, db_maintenance_plan, custom_localization, custom_skin_for_ipad, custom_server_version, zendesk_id, StartDate, EndDate)
values (new.id, new.name, new.current_version, new.db_maintenance_plan, new.custom_localization, new.custom_skin_for_ipad, new.custom_server_version, new.zendesk_id, N, NULL);
end;

count number of hierarchical childrens in sql

I have a table that stores parent and left child and right child information. How do i count number of children belongs that parent?
for example my table structure is:
parent left right
--------------------
1 2 3
3 4 5
4 8 9
5 10 11
2 6 7
9 12 null
How do I count number of sub nodes for any parent. For example 4 contains following hierarchical child nodes - 8,9,12 so number of children are 3.
3 contains following sub nodes -> 4,5,10,11,8,9,12 so total number of children 7.
How do I achieve this using SQL query?
create table mytable
( parent int not null,
cleft int null,
cright int null
)
insert into mytable (parent,cleft,cright) values (1,2,3);
insert into mytable (parent,cleft,cright) values (2,6,7);
insert into mytable (parent,cleft,cright) values (3,4,5);
insert into mytable (parent,cleft,cright) values (4,8,9);
insert into mytable (parent,cleft,cright) values (5,10,11);
insert into mytable (parent,cleft,cright) values (6,null,null);
insert into mytable (parent,cleft,cright) values (7,null,null);
insert into mytable (parent,cleft,cright) values (8,13,null);
insert into mytable (parent,cleft,cright) values (9,12,null);
insert into mytable (parent,cleft,cright) values (10,null,null);
insert into mytable (parent,cleft,cright) values (12,null,null);
insert into mytable (parent,cleft,cright) values (13,null,17);
insert into mytable (parent,cleft,cright) values (17,null,null);
DELIMITER $$
CREATE procedure GetChildCount (IN parentID INT)
DETERMINISTIC
BEGIN
declare ch int;
declare this_left int;
declare this_right int;
declare bContinue boolean;
declare count_needs_scan int;
create temporary table asdf999 (node_id int,processed int);
-- insert into asdf999 (node_id,processed) values (1,0);
-- update asdf999 set processed=1;
SET ch = parentID;
set bContinue=true;
while bContinue DO
-- at this point you are sitting at a ch (anywhere in hierarchy)
-- as you are looping and getting/using children
-- save non-null children references: -----------------------------
select cleft into this_left from mytable where parent=ch;
if !isnull(this_left) then
insert asdf999 (node_id,processed) select this_left,0;
end if;
select cright into this_right from mytable where parent=ch;
if !isnull(this_right) then
insert asdf999 (node_id,processed) select this_right,0;
end if;
-- -----------------------------------------------------------------
select count(*) into count_needs_scan from asdf999 where processed=0;
if count_needs_scan=0 then
set bContinue=false;
else
select node_id into ch from asdf999 where processed=0 limit 1;
update asdf999 set processed=1 where node_id=ch;
-- well, it is about to be processed
end if;
END WHILE;
select count(*) as the_count from asdf999;
drop table asdf999;
END $$
DELIMITER ;
call GetChildCount(2); -- answer is 2
call GetChildCount(4); -- answer is 5
I could supply a version that creates a dynamically named table (or temp table) and clobbers it at end if you want . "dynamic sql / prepare statment" inside of a procedure. that way users won't step on each other with shared use of the work table asdf999. so this is not production ready. but the above gives you an idea of the concept

Mysql: Insert row with default new data for each user

Grateful for help with a tricky (for me) query. I am very much a mysql beginner.
The table (v3_community_fields_values) has the following columns:
id (unique id of the row, autoincrements)
user_id (the id of the user)
field_id (the id of the field type)
value (the values of that
field)
access
I have been scratching my head how to write a query that inserts a row for each user where:
a) no such row already exists, and
b) another user row exists with [field_id] of 45 (45 = user profile type) and the value of either 5 or 6
the new row should contain: [id],[user_id],'75','foobar'
Very grateful for your help.
This does it (tested):
insert into v3_community_fields_values
(user_id, field_id, value)
select user_id, 75, 'foo'
from v3_community_fields_values
where field_id = 45
and value in ('5', '6')
and user_id not in (
select user_id
from v3_community_fields_values
where field_id = 75)
See live demo on SQLFiddle.
I don't know how to implement using one sql statement, but this store procedure may help:
delimiter //
drop procedure if exists insert_something//
create procedure insert_something ()
begin
DECLARE v_user_id int default 0;
DECLARE stop int default 0;
DECLARE cursor1 CURSOR FOR select DISTINCT user_id from v3_community_fields_values where field_id = 45;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET stop=1;
OPEN cursor1;
FETCH cursor1 INTO v_user_id;
while stop <> 1 do
insert into v3_community_fields_values (user_id, field_id, `value`) values (v_user_id, '75', 'foobar');
FETCH cursor1 INTO v_user_id;
end while;
CLOSE cursor1;
end //
call insert_something()//