I have 3 tables:
Module:
id_module | name
--------------------
1 users
2 roles
...
Profile:
id_profile | name
--------------------
1 admin
2 promoter
And Permission
id | id_profile | id_module | read | write | modify | delete
---------------------------------------------------------------
What I want is a trigger is to fill the table permission every time I insert a new module ... but for every existing profile.
Thus:
INSERT INTO `module` (`name`) VALUES
('user'),
('roles');
Existing profiles admin and promoter permission table would be filled this way:
id | id_profile | id_module | read | write | modify | delete
---------------------------------------------------------------
1 1 1 0 0 0 0
2 1 2 0 0 0 0
3 2 1 0 0 0 0
4 2 2 0 0 0 0
For this, I created the following trigger:
CREATE TRIGGER `create_permission` AFTER INSERT ON `module`
FOR EACH ROW
BEGIN
DECLARE `my_profile` INT(11) ;
SELECT id_profile INTO `my_profile` FROM profile WHERE state <>3;
INSERT INTO `permission` (`id_profile`, `id_module`, `read`, `write`, `modify`, `delete`) VALUES(`my_profile`, NEW.`id_module `,0,0,0,0);
END;
It works well when there is a profile ... when more than one when I return "Result consisted of more than one row"
I read that this problem arises when you declare a variable that supports a single value... You can insert records for each id of the table profile?
As you found out, you may only assign scalar values to a variable.
To iterate over the many results of a SELECT query, you need to use a CURSOR.
I personnaly dislike the cumbersome CURSOR syntax. In your particular case, I would advise the much simpler INSERT INTO...SELECT trick:
INSERT INTO permission (id_profile, id_module, read, write, modify, delete)
SELECT id_profile, NEW.id_module,0,0,0,0 FROM profile WHERE state <> 3;
Related
I have a table which consist of columns person_id, level_id, is_admin
person_id | level_id | is_admin
--------------------------------
1 | 1 | 1
1 | 2 | 0
3 | 2 | 1
In the server side, I have a function that accepts a request data which is an array of objects:
[
{person_id: 5, level_id: 1, is_admin: 1},
{person_id: 1, level_id: 2, is_admin: 0}
]
What I want to achieve is that, delete rows from the table whose values of columns person_id, level_id, is_admin does not exists in the post request data.
For example, the expected output of the delete query:
person_id | level_id | is_admin
--------------------------------
1 | 1 | 1
3 | 2 | 1
Notice that the second row is deleted.
EDIT: You might wonder delete entries that do not exists in post data, yes that's right. because the function meant to insert things in the table and delete existing rows that does not exist in the post data.
My current delete query is:
$delete = "
DELETE FROM pivotTable
WHERE NOT EXISTS (
SELECT * FROM (
SELECT
{$personId} AS person_id,
{$levelId} AS level_id,
{$isAdmin} AS is_admin
) as delTemp
);
";
$this->pdo->exec($delete);
no error, but it seems that it's not deleting the row in the database.
Easiest way to debug this would be to run the query as a SELECT:
SELECT FROM pivotTable
WHERE NOT EXISTS (
SELECT * FROM (
SELECT
{$personId} AS person_id,
{$levelId} AS level_id,
{$isAdmin} AS is_admin
) as delTemp
);
After this you can check if the rows you want to delete are correct.
I would also recommend looking into using WHERE NOT IN
as in:
DELETE FROM pivotTable
WHERE (person_id, level_id, is_admin) NOT IN ((5,1,1), (1,2,0));
Also it seems that you aren't using prepared statements which will lead you to be vulnerable to SQL Injection, I would recommend reading on prepared statements here:
https://phpdelusions.net/pdo
I want to add a new record in a table if duplicate value enters in a unique field. I don't want to update the existing one but want to add a new record by modifying the unique field value.
Is this possible in mysql?
EDIT:
Edited after user comment on this post:
You need write table locking on both of those two processes.
A WRITE lock has the following features:
The only session that holds the lock of a table can read and write data from the table.
Other sessions cannot read data from and write data to the table until the WRITE lock is released.
Also look at SQL UNIQUE Constraint
BEFORE EDIT:
Yes it is possible. And it took me awhile to figure it out. I build this on your input and compering values as test1, test2 etc, where test is always the same and has trailing number. As you specified.
It can be done as MySQL TRANSACTION in 4 steps.
Lets say you have table testT where name is unique to insure we have no doubles.
| id | name |
| --- | ----- |
| 1 | test1 |
| 2 | test3 |
And you want to insert a new item with name test1 we set is as:
SET #newName = 'test1';
Then we need to check if it already exists in table:
SELECT #check:=COUNT(*) FROM testT WHERE name = #newName;
We do a count here to get true or false and save it as #check here so we can compare it later. This will result into 1 row as test1 already exists in table.
Next we do another selection to get the highest number of test* and store it as #number, this next query selects all tests and does a SUBSTRING after 4 latter's giving us all numbers after first 4 latter's. (99999999999) numbers actually just to be sure we don't miss any but in our case result is only "3" because that is last record "test3" in table.
SELECT
#number:= SUBSTRING(name,5,99999999999)
FROM testT;
Now we can do an insert:
INSERT INTO testT(name)
VALUES
(
IF(#check = "", #newName , CONCAT(LEFT(#newName,4),RIGHT(#number,1)+1)
)
);
This tries to insert our #newName into table under IF condition, and that is if our #check is empty then he will insert #newName, if not it will take word test out of string and append a highest #number from earlier and add + 1 too it.
So result for #newName = 'test1' is below. If you change this into #newName = 'test3' result wold be same new insert test4.
**Schema (MySQL v5.7)**
SET #newName = 'test1';
---
**Query #1**
SELECT * FROM testT
ORDER BY id;
| id | name |
| --- | ----- |
| 1 | test1 |
| 2 | test3 |
| 3 | test4 |
---
And if you change it in ANY test* that number does not already exists it will insert it normally. In case below: #newName = 'test6'
SET #newName = 'test6';
**Query #1**
SELECT * FROM testT
ORDER BY id;
| id | name |
| --- | ----- |
| 1 | test1 |
| 2 | test3 |
| 3 | test6 |
This way an insert will always be made.
You can play with this here : View on DB Fiddle just by changing SET #newName = 'test6'
I am no expert and it took me couple of hours to figure this way out, as I wanted to know if this was even possible.
And I would appreciate if any other user can suggestion any other way or improve my method.
I have a table of an app setting that looks like this:
Code | Value |
---------------------
MAC_ADDR | 'SAMPLE'|
PC_OPT | 0 |
SHOW_ADDR | 1 |
Then I'm receiving a json in my trigger function like this:
{MAC_ADDR: 'NEWADDR', PC_OPT: 1, SHOW_ADDR: 0}
How do I perform an update based on all the keys from my json?
you can just use json_populate_record, eg:
t=# create table tj("MAC_ADDR" text, "PC_OPT" int, "SHOW_ADDR" int);
CREATE TABLE
t=# insert into tj select 'SAMPLE',0,1;
INSERT 0 1
t=# select * from tj;
MAC_ADDR | PC_OPT | SHOW_ADDR
----------+--------+-----------
SAMPLE | 0 | 1
(1 row)
t=# update tj set "MAC_ADDR"=j."MAC_ADDR", "PC_OPT"=j."PC_OPT", "SHOW_ADDR"=j."SHOW_ADDR"
from json_populate_record(null::tj,'{"MAC_ADDR": "NEWADDR", "PC_OPT": 1, "SHOW_ADDR": 0}') j
where true;
UPDATE 1
t=# select * from tj;
MAC_ADDR | PC_OPT | SHOW_ADDR
----------+--------+-----------
NEWADDR | 1 | 0
(1 row)
keep in mind - you did not specify PK or other column to update rows so all rows will be updated in example above. Which suits your data sample, but would not in case of more data
Update
I misunderstood the question, in (code, value) table it's even easier, eg:
update some_tbl
set "Value" = '{"MAC_ADDR": "NEWADDR", "PC_OPT": 1, "SHOW_ADDR": 0}'::json->'MAC_ADDR'
where "Code"='MAC_ADDR'
o again, using the code above you can map update with json keys...
I have two tables in a database named as follows:
1. state_master
+---------------+-------------------+
| state_id | state_name |
+---------------+-------------------+
| 1 | new |
| 2 | assigned |
| 3 | in_progress |
| 4 | on_hold |
| 5 | closed |
+---------------+-------------------+
2. store_complaint_state_count
+----------+-----+--------+------------+-------+------+
| store_id | new |assigned| in_progress|on_hold|closed|
| 101 | 1 |2 | 2 |0 |0 |
| 102 | 5 |4 | 1 |0 |2 |
+----------+-----+--------+------------+-------+------+
Now I want to add another row in state_master state_id =6 and state_name=reopen.
I want to create a trigger which can alter table store_complaint_state_count and add column reopen in it.
I've created a procedure:
CREATE DEFINER=`root`#`localhost` PROCEDURE `alterTablestorewisecomplaintcount`(in state int )
BEGIN
alter table storewisecomplaintcount
add column state INT UNSIGNED ZEROFILL NOT NULL DEFAULT 0;
END
And a trigger:
CREATE DEFINER = CURRENT_USER
TRIGGER `nxtlife_sfcms_db_v2`.`complaint_state_AFTER_INSERT`
AFTER INSERT ON `complaint_state`
FOR EACH ROW
BEGIN
call alterTablestorewisecomplaintcount(new.state_value);
END
But it throws an error at the time of insertion:
ERROR 1422: 1422: Explicit or implicit commit is not allowed in stored function or trigger.
you are not allowed to do ALTER or DROP operation on triggers. these are the implicit commits that you see in the error message (see more details here)
Even if it would be possible by some workaround, this is wrong design. If you could do what you wanted- the first insert to the table would add the column, and from then on- all other inserts would have failed since the column already exists
Trigger shouldn't be used that way; instead of every time altering the able and adding a column; you should use a single column say Status which will contain all the different status values like
store_complaint_state_count: Status varchar(20);
Status
new
assigned
in_progress
on_hold
closed
reopen
Then you can modify your trigger code to populate that value to store_complaint_state_count table
CREATE DEFINER = CURRENT_USER
TRIGGER `nxtlife_sfcms_db_v2`.`complaint_state_AFTER_INSERT`
AFTER INSERT ON `complaint_state`
FOR EACH ROW
BEGIN
INSERT INTO store_complaint_state_count (store_id, `Status`) VALUES(105, new.state_name);
END
Hi you can create table and log all events in table.
create table dbLOG (Id Int Identity(1,1),PostTime
VARCHAR(50),ServerName VARCHAR(25),UserName VARCHAR(15),CommandText
VARCHAR(MAX))
go
CREATE TRIGGER [db_LOG]
ON DATABASE
FOR create_table,alter_table,drop_table
,create_PROCEDURE, alter_PROCEDURE,drop_PROCEDURE
,ALTER_function,create_function,drop_function
,ALTER_trigger,create_trigger,drop_trigger
AS
SET NOCOUNT ON
DECLARE #xEvent XML
SET #xEvent = eventdata() --capture eventdata regarding SQL statement user have fired
INSERT INTO dbLOG VALUES(
REPLACE(CONVERT(VARCHAR(50), #xEvent.query('data(/EVENT_INSTANCE/PostTime)')),'T', ' '),
CONVERT(VARCHAR(25), #xEvent.query('data(/EVENT_INSTANCE/ServerName)')),
CONVERT(VARCHAR(15), #xEvent.query('data(/EVENT_INSTANCE/UserName)')),
CONVERT(VARCHAR(MAX), #xEvent.query('data(/EVENT_INSTANCE/TSQLCommand/CommandText)'))
)
table users as below
--------------------
portal_id | user_id
1 | 100
1 | 101
1 | 102
1 | 103
---------------------
SELECT group_concat(user_id) as toUserIds FROM users where portal_id=1;
after am getting in toUserIds is 100,101,102,103
after i want insert doc_user_xref table as below(same doc id with different user id )
insert into doc_user_xref(doc_id,user_id)values(5211,100);
insert into doc_user_xref(doc_id,user_id)values(5211,101);
insert into doc_user_xref(doc_id,user_id)values(5211,102);
insert into doc_user_xref(doc_id,user_id)values(5211,103);
In above insert value i need loop or iterator.
Don't use GROUP_CONCAT(), just use INSERT ... SELECT:
INSERT INTO doc_user_xref
(doc_id, user_id)
SELECT 5211, user_id
FROM users
WHERE portal_id = 1