Related
Background: I am building a Springboot+mySQL app that uses Spring Data JPA native queries (i.e. #Query(value = "...", nativeQuery = true) to retrieve database data.
We have to use the same SQL in every query we write to enable certain functionality (this can be seen in the example below). And wanted a way to remove this duplicate SQL and instead only declare it once. An option presented was to pass a dynamic SQL string into a stored procedure and then build a prepared statement by concatenating this dynamic SQL string with our static shared SQL string (show in example below).
Question:
This doesn't seem like a good idea to me, but I am not knowledgeable enough about databases to given the exact technical reasons. Is the below example safe, reasonable, and best-practice? Are there ways to mitigate issues with this approach, or are there other approaches to use instead?
Basic setup code:
-- create tables
CREATE TABLE docs (
id INTEGER PRIMARY KEY,
rev INTEGER NOT NULL,
content VARCHAR(30) NOT NULL
);
CREATE TABLE more_docs (
id INTEGER PRIMARY KEY,
more_content VARCHAR(30) NOT NULL
);
CREATE TABLE docs_metadata (
id INTEGER PRIMARY KEY,
letter VARCHAR(30) NOT NULL
);
CREATE TABLE metadata_nums (
id INTEGER PRIMARY KEY,
metadata_id INTEGER,
num INTEGER NOT NULL
);
-- insert some values
INSERT INTO docs VALUES (1, 1, 'abc');
INSERT INTO docs VALUES (2, 1, 'def');
INSERT INTO docs VALUES (3, 2, 'ghi');
INSERT INTO docs VALUES (4, 1, 'jkl');
INSERT INTO more_docs VALUES (1, 'aaa');
INSERT INTO more_docs VALUES (2, 'bbb');
INSERT INTO more_docs VALUES (3, 'ccc');
INSERT INTO more_docs VALUES (4, 'ddd');
INSERT INTO docs_metadata VALUES (1, 'a');
INSERT INTO docs_metadata VALUES (2, 'b');
INSERT INTO docs_metadata VALUES (3, 'c');
INSERT INTO docs_metadata VALUES (4, 'd');
INSERT INTO metadata_nums VALUES (1, 1, 5);
INSERT INTO metadata_nums VALUES (2, 1, 6);
INSERT INTO metadata_nums VALUES (3, 2, 5);
INSERT INTO metadata_nums VALUES (4, 2, 6);
INSERT INTO metadata_nums VALUES (5, 3, 5);
INSERT INTO metadata_nums VALUES (6, 3, 6);
INSERT INTO metadata_nums VALUES (7, 4, 5);
INSERT INTO metadata_nums VALUES (8, 4, 6);
Approach in question:
-- create stored procedure
DELIMITER //
CREATE PROCEDURE FILTER_EVAL (IN dynamic_statement TEXT, IN num INT, IN letter VARCHAR(1))
BEGIN
SET #dynamic_statement := CONCAT("SELECT X.* FROM (", dynamic_statement, ") X INNER JOIN (SELECT DM.*, MN.num FROM docs_metadata DM INNER JOIN metadata_nums MN ON DM.id = MN.metadata_id) M ON X.id = M.id WHERE M.num = ", num, " AND M.letter = '", letter, "'");
PREPARE prepared_statement FROM #dynamic_statement;
EXECUTE prepared_statement;
DEALLOCATE PREPARE prepared_statement;
END//
DELIMITER ;
-- fetch some values
CALL FILTER_EVAL("SELECT * FROM docs WHERE rev = 1", 5, 'b')
I am trying to make a stored procedure that takes a name/or number and returns a specific value for that name/number
So in this code, I am trying to write a stored procedure taht takes a company name or the cvr number and return the degree of compliance for that company
DROP DATABASE IF EXISTS DRCS;
CREATE DATABASE DRCS;
USE DRCS;
CREATE TABLE Companies (
ID INT not null,
Name varchar(255),
primary key (ID)
);
CREATE TABLE CVR (
ID INT not null,
CVR_Number INT
);
CREATE TABLE Compliance (
ID INT not null,
Complaince_level varchar(255)
);
CREATE TABLE GDPR (
ID INT not null,
GDPR_steps varchar(255)
);
INSERT INTO Companies VALUES
(1, 'TDC'),
(2, 'MAERSK'),
(3, 'LEGO'),
(4, 'SAS'),
(5, 'Carlsberg');
INSERT INTO CVR VALUES
(11, '14773908'),
(12, '32345794'),
(13, '47458714'),
(14, '56994912'),
(15, '61056416');
INSERT INTO Compliance VALUES
(21, '10%'),
(22, '20%'),
(23, '40%'),
(24, '60%'),
(25, '80%');
INSERT INTO GDPR VALUES
(31, '1. Awareness'),
(32, '2. Information you hold'),
(33, '3. Communication privacy information'),
(34, '4. Individuals rights'),
(35, '5. Subject access requests');
#HERE MY PROBLEM STARTS
DELIMITER $$
CREATE PROCEDURE DoC()
BEGIN
SELECT * FROM Companies , CVR, Compliance, GDPR;
END $$
DELIMITER ;
CALL DoC;
There is no relationship between the four tables!
Create rows with common id like id = 1,2,3,4 for all tables and then do a join and then try to list the fields you want to view.
INSERT INTO Companies VALUES
(1, 'TDC'),
(2, 'MAERSK'),
(3, 'LEGO'),
(4, 'SAS'),
(5, 'Carlsberg');
INSERT INTO CVR VALUES
(1, '14773908'),
(2, '32345794'),
(3, '47458714'),
(4, '56994912'),
(5, '61056416');
INSERT INTO Compliance VALUES
(1, '10%'),
(2, '20%'),
(3, '40%'),
(4, '60%'),
(5, '80%');
INSERT INTO GDPR VALUES
(1, '1. Awareness'),
(2, '2. Information you hold'),
(3, '3. Communication privacy information'),
(4, '4. Individuals rights'),
(5, '5. Subject access requests');
select c.id,v.cvr_Number from Companies c, cvr v where c.id = v.id
Similarly add more tables by joining with id. You don't need a procedure to this and a procedure is not designed for this.
You can make with union query for all table. But all select query of clomum count should be same.
DELIMITER $$
CREATE PROCEDURE DoC()
SELECT A.id,A.name FROM
( SELECT id as id , name as name FROM Companies
UNION ALL
SELECT id as id , name as name FROM CVR
UNION ALL
SELECT id as id , name as name FROM Compliance
UNION ALL
SELECT id as id , name as name FROM GDPR) as A
END $$
DELIMITER ;
I need to blank SSN's from an audit/log table and we can't alter the application.
The application produces an audit/log into table 'redcap_log_event', column 'sql_log'. This column is simply a long string of SQL used by the application when each command was performed within the app.
This is an example of the sql_log column when you copy and paste it out, the carriage returns are in the string of SQL from the column. The inserts end up in the sql_log column from the application -
INSERT INTO redcap_data (project_id, event_id, record, field_name, value, instance) VALUES (6, 21, 'Ashby, zbn', 'study_id', '123456789', NULL);
INSERT INTO redcap_data (project_id, event_id, record, field_name, value, instance) VALUES (6, 21, 'Ashby, zbn', 'cmr_date', '2015-09-02', NULL);
INSERT INTO redcap_data (project_id, event_id, record, field_name, value, instance) VALUES (6, 21, 'Ashby, zbn', 'first_name', 'bnm', NULL);
INSERT INTO redcap_data (project_id, event_id, record, field_name, value, instance) VALUES (6, 21, 'Ashby, zbn', 'last_name', 'asdfggh', NULL);
INSERT INTO redcap_data (project_id, event_id, record, field_name, value, instance) VALUES (6, 21, 'Ashby, zbn', 'dob', '2015-09-02', NULL);
INSERT INTO redcap_data (project_id, event_id, record, field_name, value, instance) VALUES (6, 21, 'Ashby, zbn', 'age_at_cmr', '21', NULL);
INSERT INTO redcap_data (project_id, event_id, record, field_name, value, instance) VALUES (6, 21, 'Ashby, zbn', 'social_security_number', '987582154', NULL);
INSERT INTO redcap_data (project_id, event_id, record, field_name, value, instance) VALUES (6, 21, 'Ashby, zbn', 'sex', '1', NULL);
INSERT INTO redcap_data (project_id, event_id, record, field_name, value, instance) VALUES (6, 21, 'Ashby, zbn', 'ethnicity', '1', NULL);
INSERT INTO redcap_data (project_id, event_id, record, field_name, value, instance) VALUES (6, 21, 'Ashby, zbn', 'demographics_abef_complete', '2', NULL);
INSERT INTO redcap_data (project_id, event_id, record, field_name, value, instance) VALUES (6, 21, 'Ashby, zbn', 'participant_id', 'asdfggh, zbn', NULL)
I am wanting to eventually blank out the 9 digit SSN value from within the above data via a trigger on the table but for now i'm struggling to get a substring, regex, locate to work how i need it to - The position of text 'social_security_number' within column sql_log will always be different because of the firstname and lastname inserts before it, so i need to somehow find a varying position on the right side of 'social_security_number'
Any ideas appreciated!
Thanks for the downvotes, friendly community i see. Anyway, this is what i finished with:-
USE `ctsiredcap`;
DROP procedure IF EXISTS `SsnBlankOutInsert`;
DELIMITER $$
USE `ctsiredcap`$$
CREATE PROCEDURE `SsnBlankOutInsert` ()
BEGIN
UPDATE redcap_log_event
SET
sql_log = CONCAT(SUBSTR(sql_log,
1,
LOCATE('social_security_number', sql_log) + 25),
'*********',
SUBSTR(sql_log,
LOCATE('social_security_number', sql_log) + 35)),
data_values = CONCAT(SUBSTR(data_values,
1,
LOCATE('social_security_number', data_values) + 25),
'*********',
SUBSTR(data_values,
LOCATE('social_security_number', data_values) + 35))
WHERE
ts > REPLACE(REPLACE(REPLACE(NOW() - INTERVAL 10 MINUTE,
'-',
''),
':',
''),
' ',
'')
AND LOCATE('social_security_number', sql_log) > 0;
END$$
DELIMITER ;
Not pretty by any means but it seems to be doing the job
Thanks,
Allan
Write the DDL script to make a stored procedure that returns data needed to display a grade book screen for a professor. The only input for the stored procedure is a ClassId. Outputs need to include student names and grades for all assignments as well as a calculated overall grade for the class for each student. Provide an example calling this new stored procedure, passing it parameter values of your choice. Include a screenshot of the output.
I am unsure how to write this in stored procedure. I don't really understand what is my parameter. I know my input is ClassID and my outputs are student names and grades. Itried this below but keep getting error.
CREATE DEFINER=`root`#`localhost` PROCEDURE `totalAssignmentGradesbyCourse`(aAssignment dec)
SELECT (ClassID) AVG('Assignment1', 'Assignment2',Assignment 3)
FROM student_classes sc
INNER JOIN LastNames, ClassID
ON sc.student_classes
WHERE Assignment=aAssignment
GROUP BY Assignment;
END
fields for Table student_classes:
INSERT INTO student_classes
VALUES (NULL, 45, 36, '2012-01-01', '90', '100', '80', '90', '3.0');
SELECT * FROM student_classes;
INSERT INTO student_classes
VALUES (NULL, 46, 37, '2012-05-01', '60', '70', '80', '90', '2.7');
SELECT * FROM student_classes;
INSERT INTO student_classes
VALUES (NULL, 47, 38, '2012-08-01', '100', '100', '100', '95', '4.0');
SELECT * FROM student_classes;
INSERT INTO student_classes
VALUES (NULL, 48, 39, '2012-01-01', '85', '90', '80', '92', '3.5');
SELECT * FROM student_classes;
student_classes Table Columns:StudentClassID, StudentID, ClassID, StartDate, Assignment1, Assignment2, Assignment3,Assignment4, ClassGPA.
I'm confused.
I want to insert multiple rows into different tables in my database. The statements are paired, where the second relies on data (specifically LAST_INSERT_ID()) from the first statement.
This code works, but I wonder if it can be done in a better way?
Is there a way to have it all in one single query?
<?php
mysql_query("
INSERT INTO wp_terms (term_id, name, slug, term_group)
VALUES ('', 'Test 1', 'test-1', '0')
");
mysql_query("
INSERT INTO wp_term_taxonomy (term_taxonomy_id, term_id, taxonomy, description, parent, count)
VALUES ('', LAST_INSERT_ID(), 'mycustomfield', 'Descripton 1', '0', '0')
");
mysql_query("
INSERT INTO wp_terms (term_id, name, slug, term_group)
VALUES ('', 'Test 2', 'test-2', '0')
");
mysql_query("
INSERT INTO wp_term_taxonomy (term_taxonomy_id, term_id, taxonomy, description, parent, count)
VALUES ('', LAST_INSERT_ID(), 'mycustomfield', 'Descripton 2', '0', '0')
");
mysql_query("
INSERT INTO wp_terms (term_id, name, slug, term_group)
VALUES ('', 'Test 3', 'test-3', '0')
");
mysql_query("
INSERT INTO wp_term_taxonomy (term_taxonomy_id, term_id, taxonomy, description, parent, count)
VALUES ('', LAST_INSERT_ID(), 'mycustomfield', 'Descripton 3', '0', '0')
");
mysql_query("
INSERT INTO wp_terms (term_id, name, slug, term_group)
VALUES ('', 'Test 4', 'test-4', '0')
");
mysql_query("
INSERT INTO wp_term_taxonomy (term_taxonomy_id, term_id, taxonomy, description, parent, count)
VALUES ('', LAST_INSERT_ID(), 'mycustomfield', 'Descripton 4', '0', '0')
");
?>
If it is a thing to reduce the call of mysql_query, Yes, There is a way but you have to use the mysqli for this.
The mysqli::multi_query can execute the multiple queries in once.
You also can create an INSERT trigger -
DELIMITER $$
CREATE TRIGGER trigger_wp_terms_insert
AFTER INSERT
ON wp_terms
FOR EACH ROW
BEGIN
INSERT INTO wp_term_taxonomy(term_taxonomy_id, term_id, taxonomy, description, parent, count)
VALUES ('', NEW.term_id, 'mycustomfield', CONCAT('Descripton ', SUBSTRING_INDEX(new.name, ' ', '-1')), '0', '0');
END
$$
DELIMITER ;
Then just add new rows into the wp_terms table using one bulk INSERT statement -
INSERT INTO wp_terms (term_id, name, slug, term_group) VALUES
('', 'Test 1', 'test-1', '0'),
('', 'Test 2', 'test-2', '0'),
('', 'Test 3', 'test-3', '0'),
('', 'Test 4', 'test-4', '0');
In my opinion, using a stored procedure would be one of the best ways to do it:
CREATE DEFINER=`root`#`localhost` PROCEDURE `InsertWpTermAndTaxonomy`
( IN paramWPT_id <<PlaceTheRightDataTypeHere>>
, IN paramWPT_name <<PlaceTheRightDataTypeHere>>
, IN paramWPT_slug <<PlaceTheRightDataTypeHere>>
, IN paramWPT_termgroup <<PlaceTheRightDataTypeHere>>
, IN paramWPTX_id <<PlaceTheRightDataTypeHere>>
, IN paramWPTX_taxonomy <<PlaceTheRightDataTypeHere>>
, IN paramWPTX_description <<PlaceTheRightDataTypeHere>>
, IN paramWPTX_parent <<PlaceTheRightDataTypeHere>>
, IN paramWPTX_count <<PlaceTheRightDataTypeHere>>
BEGIN
INSERT INTO wp_terms (term_id, name, slug, term_group)
VALUES (paramWPT_id, paramWPT_name, paramWPT_slug, paramWPT_termgroup)
INSERT INTO wp_term_taxonomy (term_taxonomy_id, term_id, taxonomy, description, parent, count)
VALUES (paramWPTX_id, LAST_INSERT_ID(), paramWPTX_taxonomy, paramWPTX_description, paramWPTX_parent, paramWPTX_count)
END
Then use code like this (though I choose PDO over MYSQL or MYSQLI):
# using an array which you can populate to contain all the
# necessary parameters...
$term_and_taxonomy_array_values = array('', 'Test 4', 'test-4', '0', '', 'mycustomfield', 'Descripton 4', '0', '0');
$sql = 'CALL InsertWpTermAndTaxonomy (?,?,?,?,?,?,?,?,?)';
$stmt = $pdo->prepare($sql);
foreach($term_and_taxonomy_array_values as $key => $value) {
$stmt->bindValue($key + 1, $value);
}
$stmt->execute();
Hope you can look into PDO, but mysqli should be able to do this as well. I coded the procedure so that it can be flexible enough to take whatever acceptable parameters for both tables, even though it seemed like you have constant 0s and ''s
If you want to further make this dynamic, say for example you can build your array of parameter arrays, then you'll just need one more foreach to wrap it up...
add an additional column to wp_terms, you can name the column as session_key, this is to store an unique value for each insertion, an example of unique value can be current timestamp concat with some WP session value
upon insertion, assign the session_key to an unique value and included into insert statement
combine all the insert into wp_terms into single insert ... values (...,session_key), (..., $session_key)
factor in error handling
after you have insert successfully, you can make use on the unique value you have created at step 2 to insert into wp_term_taxonomy select ... where session_key = $session_key
with this small trick, possibilities hitting an error is quite minimum