Add column with FK to an existing table in MySQL 5.6 - mysql

Good day everyone. Hope someone can helps me in my experiments.
I have a simple Table for my needs and it's even have some data inside.
CREATE TABLE IF NOT exists main.test (
ID INT(11) NOT NULL,
NAME varchar(30) NOT NULL,
DATE_CREATED timestamp NOT NULL,
PRIMARY KEY (ID));
But then I should update this table with adding column FK_.
How can I check if table had already has field FK_?
If such column is not exist do:
ALTER TABLE main.test
ADD COLUMN FK INT(11),
ADD FOREIGN KEY (FK)
REFERENCES test2(ID_test2)

As I use java decision of my problem was using ResultSet.
ResultSet set = statement.executeQuery(query);
set.next();
int result = set.getInt(1); //it always return only one row
set.close();
And this is my sql-query:
SELECT COUNT(COLUMN_NAME) FROM information_schema.COLUMNS
WHERE
TABLE_SCHEMA = 'main'
AND TABLE_NAME = 'test'
AND COLUMN_NAME = 'FK";
When I'll get a result I can decide what query I should to use.

Related

Check if an element (primary key) is involved somewhere else in the database

I would like to check if an element is implicated in another database table as a foreign key, not knowing which table it is involved in.
For now I have only managed to find out which are the tables where the element stands as a foreign key, using the following query:
SELECT TABLE_NAME
FROM information_schema.REFERENTIAL_CONSTRAINTS
WHERE REFERENCED_TABLE_NAME = 'funzione_operativa_sedi_e_recapiti';
The table is made up as follows:
CREATE TABLE IF NOT EXISTS funzione_operativa_sedi_e_recapiti (
id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(255) NOT NULL UNIQUE,
descrizione VARCHAR(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Summarizing, I would like my query to show me only the tables where that item exists as a foreign key. If the element is not in those tables (for example those tables are empty), there is no need for me to retrieve them.
Thanks in advance.
The following query will work:
SELECT
table_name, column_name
FROM
information_schema.key_column_usage
WHERE
referenced_table_name = <table name>
and referenced_column_name = <desired column>

How to set AUTO_INCREMENT PRIMARY KEY when using 'AS SELECT'

I am trying to create a temporary table and right now I am doing something like this:
CREATE IF NOT EXISTS TABLE tempdb.student AS (SELECT * FROM student LIMIT 0)
then
ALTER TABLE tempdb.student MODIFY id INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY
However, I need to find a way to do this without the ALTER statement because both statements are getting ran together and if the table has already been created, I am getting an error because of the multiple primary keys. Any advice? Thanks
You can define columns, indexes, table options, and partitioning before the AS keyword.
CREATE IF NOT EXISTS TABLE tempdb.student (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(10),
enroll_date DATE,
...etc...
)
AS SELECT * FROM student LIMIT 0
This is the only way you can set some column or table options, such as auto-increment.
But it means you have to define all the columns, not just id.
Since you are using LIMIT 0 it means you don't actually want any rows from the source table. I guess you are just trying to duplicate the table structure, but none of the data.
In that case, you might like to use the following instead:
CREATE IF NOT EXISTS TABLE tempdb.student LIKE student;
This duplicates other column and table options, and indexes, and partitions. The new table will have the AUTO_INCREMENT option for its primary key.
You can explicitly check if the table exists (you need to be careful if the code can be executed in parallel):
SELECT
COUNT(*) INTO #exists
FROM
information_schema.tables
WHERE
table_schema = 'tempdb'
AND table_name = 'student'
;
IF #exists = 0 THEN
CREATE TABLE tempdb.student AS (SELECT * FROM student LIMIT 0); -- or use LIKE syntax
ALTER TABLE tempdb.student MODIFY id INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY;
END IF;
If your temporary table needed for the session only, I'd use CREATE TEMPORARY TABLE ... LIKE ... and alter as required.

How to post new records in sql in an existing table where my column has all NULL values?

I have added a new Column name Age in an existing table called StudentRecords. All the values are showing Null when I do Select * From StudentRecords;
How do I update so I can give a new age to a student in my table ?
Alter Table StudentRecords Add Age varchar (40);
First of all, if you wanted the column to not be null, or a default value for all Age column data, this is the way to do so in SQL:
ALTER TABLE <YourTable>
ADD <NewColumn> <NewColumnType> NOT NULL DEFAULT <DefaultValue>
So in your case, the above would be (if you wanted the records to not be null):
ALTER TABLE StudentRecords
ADD Age VARCHAR(40) NOT NULL
If you wanted to change/modify an existing record though, here's the SQL code to do that (if you wanted to only alter the record with 4 as an ID:
UPDATE StudentRecords
SET Age = "This person is old"
WHERE ID = 4;

Add an autoincrement primary key to a table created with a SELECT statement

This is a syntax question, I don't know if what I'm thinking is possible. I can't find anything about it.
CREATE TABLE IF NOT EXISTS newTable
SELECT something1, something2, something3 FROM someTable;
Using this syntax to create a new table, can I add a primary key to this? Something like this:
CREATE TABLE IF NOT EXISTS newTable
SELECT id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, something1, something2, something3 FROM someTable;
I have a large and complex dynamic query that returns me te exact results I need. All that's left is to add these results an autoincrement primary key!
Because the query is dynamic, I don't know the number of columns I'll get, so I can't just:
DROP TABLE IF EXISTS newTable;
CREATE TABLE newTable(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
something1 VARCHAR(255),
something2 VARCHAR(255),
something3 VARCHAR(255)
) ENGINE=MEMORY;
Thanks in advance for any given advice!
Looks like you will have to run multiple steps to do this:
Create a table with the primary key
Add the columns to this table using a stored procedure (could be merged with step 1)
To do this you use the information schema. This will look as follows (dummy code, no mysql handy to debug anything):
CREATE PROCEDURE create_table(table_name VARCHAR(200))
BEGIN
declare cursor table_columns as SELECT COLUMN_NAME, DATA_TYPE,
IS_NULLABLE, COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'table_name'
LOOP
concatenate create table statement
END LOOP;
execute create table statement
END

Need help with mysql schema design - current schema requires dynamic sql within a trigger

I imagine that I have designed my database badly, but I'm currently stumped by the fact that I need to use dynamic sql in a trigger and that's making mysql unhappy.
The context is that I have created a membership database with several dozen tables, the main one of which is the 'member' table with a unique primary key 'id'. There are a number of other tables which have foreign keys referring to the member.id field.
Because the data has been gathered over many years and with little dupe-control, there is another field in the 'member' table called 'superseded_by', which contains the id of the member who supersedes this one. By default, superseded_by is set to be the member_id. Any one whose superseded_by <> id is deemed to be a dupe.
Now the tricky part... when we identify a dupe, we want to set the superseded_by field to point to the new primary member and update all the tables with foreign keys pointing to the now redundant member id. I have tried to do this using an after update trigger... and then I've tried to be clever by querying the foreign keys from the information_schema and using dynamic sql to update them.
This clearly doesn't work (Error Code: 1336 Dynamic SQL is not allowed in stored function or trigger).
I'm assuming there is a better way to design the schema / handle dupes which I haven't thought of.
Help please...
CODE SNIPPET:
-- ---
-- Table 'member'
-- ---
DROP TABLE IF EXISTS member;
CREATE TABLE member (
id INTEGER AUTO_INCREMENT,
superseded_by INTEGER DEFAULT NULL,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
date_of_birth DATE DEFAULT NULL,
gender ENUM('M', 'F') DEFAULT NULL,
mailing_address_id INTEGER DEFAULT NULL,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
FOREIGN KEY (mailing_address_id) REFERENCES mailing_address (id),
FOREIGN KEY (superseded_by) REFERENCES member (id)
);
DELIMITER $$
CREATE TRIGGER set_superseded_by_on_insert BEFORE INSERT ON member FOR EACH ROW
BEGIN
SET NEW.superseded_by = NEW.id;
END$$
-- Trigger to update other tables (volunteers, donations, presenters, etc.) when member's superseded_by record is updated
-- Assumes the new superseding person exists (they should also not be superseded by anyone themselves)
CREATE TRIGGER adjust_foreign_member_keys_on_superseded_by_update AFTER UPDATE ON member FOR EACH ROW
BEGIN
DECLARE db, tbl, col VARCHAR(64);
DECLARE fk_update_statement VARCHAR(200);
DECLARE no_more_rows BOOLEAN;
DECLARE fks CURSOR FOR SELECT kcu.TABLE_SCHEMA, kcu.TABLE_NAME, kcu.COLUMN_NAME
FROM information_schema.TABLE_CONSTRAINTS tc
JOIN information_schema.KEY_COLUMN_USAGE kcu ON
tc.table_schema = kcu.table_schema AND tc.constraint_name = kcu.constraint_name
WHERE tc.constraint_type='FOREIGN KEY' AND
kcu.REFERENCED_TABLE_NAME = 'member' AND
kcu.REFERENCED_COLUMN_NAME = 'id';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows = TRUE;
IF NEW.superseded_by <> OLD.superseded_by THEN
OPEN fks;
SET no_more_rows = FALSE;
update_loop: LOOP
FETCH fks INTO db, tbl, col;
IF no_more_rows THEN
LEAVE update_loop;
END IF;
SET #fk_update_statement = CONCAT("UPDATE ", db, ".", tbl, " SET ", col, " = NEW.superseded_by WHERE ", col, " = NEW.id;");
PREPARE stmt FROM #fk_update_statement;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE fks;
END IF;
END$$
DELIMITER ;
Why are you trying to maintain duplicates in your main table? Seems like you'd be better off with a member table and a member_history table to track previous changes. You could do it by having a table that stored the field changed, date changed and the old and new values. Or you could just store the previous snapshot of the member table before updating it. For instance:
INSERT INTO member_history SELECT NULL, * FROM member WHERE id = ?
UPDATE member SET [...] WHERE id = ?
The schema for member_history would be nearly identical except that you would store member.id as member_id and have a separate primary key for each history entry. (Note: I'm glossing over the syntax a little, the NULL, * part might not work in which case you may need to explicitly name all the fields. Haven't taken the time to check it).
CREATE TABLE member (
id INTEGER AUTO_INCREMENT,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
date_of_birth DATE DEFAULT NULL,
gender ENUM('M', 'F') DEFAULT NULL,
mailing_address_id INTEGER DEFAULT NULL,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
FOREIGN KEY (mailing_address_id) REFERENCES mailing_address (id),
);
CREATE TABLE member_history (
id INTEGER AUTO_INCREMENT,
member_id INTEGER NOT NULL,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
date_of_birth DATE DEFAULT NULL,
gender ENUM('M', 'F') DEFAULT NULL,
mailing_address_id INTEGER DEFAULT NULL,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
FOREIGN KEY (member_id) REFERENCES member (id),
);
Notice that I removed the superseded_by field in the member table and the foreign key to mailing_address in the member_history table. You shouldn't need the superseded_by any more and keeping the foreign key in the member_history table isn't really necessary unless you're worried about dangling references in your history.
Ok, just a couple of thoughts on this:
superseded_by is referencing id on the same table and is in general equal to the latter - not in those cases where you were able to identify a dupe, though, in which case it would point to another already existing member's id.
Given that we can safely assume that no superseded_by field will ever hurt the foreign key constraint.
I further assume that id and superseded_by fields of dupes that have not been identified yet are equal.
So, if all of the above is true, you may bend the foreign key of the other related tables to reference superseded_by instead of id. This way you could cascade the changes made to the dupe down to the other tables and still have the exact same constraint as before.
What you think? Am I missing something?
Please note that this is an option only if you are using InnoDB rather than MyISAM.
Regards,
aefxx
Trigger and stored function in mysql have limitations that we can not use dynamic sql in both of these. I hope this helps.