Let's assume that I have mytable in mySQL
CREATE TABLE `mytable` (
`gender` enum('MALE','FEMALE','UNISEX') NOT NULL,
);
I don't want to enumerate these values at design time. I want to put them in another_table as values.
In another_table the SELECT values are:
ID NAME
== ======
01 MALE
02 FEMALE
03 UNISEX
I can define the mytable.gender as INT and combine these two tables in WHERE clause with sth like mytable.gender=another_table.id.
Can I create a database level foreign-key relationship with these enum values at design time?
Yes, you use a foreign key constraint.
That will make the database refuse inserts or updates to id values in the mytable table that doesn't exist in the another_table table, and refuse deletes from the another_table table for values that are used in the mytable table.
Related
Let's say I have these two tables. Where I insert employees to employee table coming from the staging table.
staging table:
id
employee_id
name
1
12
Paul
2
13
Kyle
employee table
id
employee_id
name
5
4
Will
6
13
Kyle
Now, on the employee table let's say I'd like to copy what's on my staging table currently, using the INSERT SELECT INTO statement, Paul will be inserted but I don't want Kyle to be inserted since he's on the employee table already(employee.employee_id is the defining column).
I know this could be done by just setting a unique or primary key, on employee_id and just using the statement ON DUPLICATE KEY UPDATE then do nothing by just setting them back to their original values.
I'm new to SQL, and I'm stuck with the solution setting a UNIQUE key and ON DUPLICATE KEY UPDATE statement, but I'd like to know how to do this without that solution I mentioned?
First of all, you should keep in mind that the decision whether to create unique or primary keys or not does not depend on how to create insert statements or such. It's a matter of what your table should do and what not.
In order to achieve your goal, you can add a where to your insert statement which excludes the already existing entries, as example:
INSERT INTO employees (id, employee_id, name)
SELECT id, employee_id, name
FROM staging
WHERE employee_id NOT IN (SELECT employee_id FROM employees)
Break the problem down into its constituent parts
get a list of employees who are in the staging table but not in the target table
insert those records only
Part 1 can be achieved with a Left Join and Where condition that the target table is null. Wrap that up as a CTE and then use it in 2)
I've an 'orders' table structure like this which contains 100,000 records:
date orderid type productsales other
01-Aug-2014 11 order 118 10.12
01-Aug-2014 11 order 118 10.12
18-Aug-2014 11 order 35 4.21
22-Aug-2014 11 Refund -35 -4.21
09-Sep-2014 12 order 56 7.29
15-Sep-2014 12 refund -56 -7.29
23-Oct-2014 13 order 25 2.32
26-Oct-2014 13 refund -25 -2.32
Now, what I want to achieve is to delete those duplicate row from my table where the orderid, type, productsales and other columns values are same to each other and keep only one row (look at the first two records for the orderid of 11).
But if the 'orderids' are same for the two records of the same 'type' of order, but the 'productsales' and 'other' columns values are different then don't delete those rows. I hope I clarified my point.
I'm looking for a mysql delete query to perform this task.
You should add an id column. If you don't want to use a temp table, you could probably do something like this (I have NOT tested this, so...):
ALTER TABLE 'orders'
ADD COLUMN 'id' INT NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY Id(id)
DELETE
FROM orders INNER JOIN
(
SELECT TOP 1 id
FROM orders
WHERE COUNT(DISTINCT date,orderid,type.productsales,other) > 1
) dupes
ON orders.id = dupes.id
May be its duplicate question to this: MySql: remove table rows depending on column duplicate values?
You can seek for the answer there.
The solution there specify that adding unique index on your possible duplicate columns with IGNORE keyword will remove all duplicates row.
ALTER IGNORE TABLE `table` ADD UNIQUE INDEX `name` (`col1`, `col2`, `col3`);
Here I also want to mention some points:
unique index does not make change in row if any columns(from index, like here 3 columns) have null as value. Ex: null,1,"asdsa" can be stored twice
same way if you have single column in unique index then multiple rows with null values(for that column) will remains in table
IGNORE keywords id depreciated now, it will not work after MySQL 5.6(may be). Now only option is to create new table by a query like this:
CREATE TABLE <table_name> AS SELECT * FROM <your_table> GROUP BY col1,col2,col3;
After that you can delete <your_table> and rename <table_name> to your table.
Here you can change the column list in Group By clause according to your need(from all columns to one column, or few columns which have duplicate values together).
The plus point is, it will work with null values also.
A really easy way to do this is to add a UNIQUE index on the 3 columns. When you write the ALTER statement, include the IGNORE keyword. Like so:
ALTER IGNORE TABLE orders ADD UNIQUE INDEX idx_name (orderid, type, productsales, other);
This will drop all the duplicate rows. As an added benefit, future INSERTs that are duplicates will error out. As always, you may want to take a backup before running something like this...
I hope this can help you.
try this.
create temp table such as temp and stored unique data,
SELECT distinct * into temp FROM Orders
then delete records of orders table table as
DELETE FROM orders
after deleted all records insert records temp into records.
INSER into RECORDS SELECT * FROM TEMP DROP TABLE TEMP
If you have completely duplicated rows, and you want to do this in SQL, then perhaps the best method is to save the rows you want in a temporary table, truncate the table, and insert the data back in:
create temporary table temp_orders as
select distinct *
from orders;
truncate table orders;
alter table orders add orderid int not null primary key auto_increment;
insert into orders;
select *
from temp_orders;
Oh, look, I also added an auto-incrementing primary key so you won't have this problem in the future. This would be a simpler process if you have a unique key on each row.
I have a source(web pages) that have common data and uncommon data that which I need to store in one table.
The data can look like this:
model: xyz, attr_1: xyz, attr_2: xyz
model: xyz, attr_3: xyz, attr_4: xyz
model: xyz, attr_1: xyz, attr_4: xyz
model: xyz, attr_1: xyz, attr_5: xyz
model: xyz, attr_15: xyz, attr_20: xyz
This data will generate this DML:
insert into table (model, attr_1, attr_2)values('xyz','xyz','xyz');
insert into table (model, attr_3, attr_4)values('xyz','xyz','xyz');
insert into table (model, attr_1, attr_4)values('xyz','xyz','xyz');
insert into table (model, attr_1, attr_5)values('xyz','xyz','xyz');
insert into table (model, attr_15, attr_20)values('xyz','xyz','xyz');
My problem is that I can't define the table before the insert commands so I can't know the columns and in every new insert I may discover new columns. I can't get all the insert commands before the actual insert. The only thing I think of is to insert every row to different table (using create table as insert into) and then use UNION ALL to create the final table. But this sound not so good idea.
EDIT I don't looking for normalized table.
The end result should be(as for the example):
table_name
id int
model varchar
attr_1 varchar
attr_2 varchar
attr_3 varchar
attr_4 varchar
attr_5 varchar
attr_15 varchar
attr_20 varchar
There's a really simple solution to this. You need to change your table:
table: model
modelName attribute value
xyz 1 xyz
xyz 2 xyz
Then when you do the INSERT, you would do:
INSERT INTO `model` (`modelName`, `attribute`, `value`) VALUES ('xyz', 1, 'xyz')
This is a normalized table structure that allows for n amount of attributes.
If you use an Array to get your data then you could use PHP's implode(', ', $array). But, you may not be using PHP. If that's the case you could always just concatenate what you're INSERTing with ,.
Right solution is to normalize your schema.
Create 2 tables: master table for main model - pretty much what you have now, but without attributes, and slave table to keep attributes. Something like this:
CREATE TABLE master (
master_id INTEGER PRIMARY KEY AUTOINCREMENT,
model VARCHAR(50)
);
CREATE TABLE attrs (
attr_id INTEGER PRIMARY KEY AUTOINCREMENT,
master_id INTEGER NOT NULL,
attr_name VARCHAR(20)
);
This schema is rather compact and has some important properties. For example, it allows you to keep arbitrary number of attributes associated with given model - it could be 0, or it could be 1000.
To insert data, you will need insert in master table first, and then to attrs table.
To retrieve data, use simple join like this:
SELECT m.model,
a.attr_name
FROM master m
JOIN attrs a ON m.model_id = a.model_id
WHERE ...
For the sake of simplicity lets say I have a table with 3 columns; id, parent_id and name. In this table id is my auto-incrementing primary key. I want to group multiple names together in this table, to do this all names in a group will share the same parent_id. If I am inserting the first name in the group I want the id=parent_id, if i am inserting another name I want to specify a specific parent_id to place that name into a specific group. It would be nice if I could define a default for that column to be the same as the id, if I specify a value for parent_id in the insert query then I would like it to use that value. I know you can set a default to be a specific static value, but can you specify the default to be the same as that row's auto-incrementing primary key? Perhaps this is a job for a trigger or stored procedure?
(I know I could obtain the primary key generated by the last insert and then update the table, but that's 2 quires I'd rather not burn.)
Thanks!
This is a job of a trigger!
CREATE TRIGGER NAME1 AFTER INSERT ON TABLE1
BEGIN
UPDATE TABLE1 SET parent_id = id WHERE (parent_id IS NULL OR parent_id = '');
END;
INSERT INTO TABLE1 (id,parent_id) VALUES (null,null); -- parent_id will be equal to id
INSERT INTO TABLE1 (id,parent_id) VALUES (null,'1'); -- parent_id will be 1
INSERT INTO TABLE1 (id,parent_id) VALUES (null,'2'); -- parent_id will be 2
I want to know if I can repopulate the autoincrement value in mysql.
Because, I have records that look similar:
ID Name
1 POP
3 OLO
12 lku
Basically , I want a way to update the ID to this
ID Name
1 POP
2 OLO
3 lku
Is there any way to do this in mysql?
Thanks.
It's not best practice to fiddle your primary keys - better to let your DB handle it itself. There can be issues if, in between the UPDATE and ALTER, another record is added. Because of this, you must LOCK the table, which might hang other queries and spike load on a busy production server.
LOCK TABLES table WRITE
UPDATE table SET id=3 WHERE id=12;
ALTER TABLE table AUTO_INCREMENT=4;
UNLOCK TABLES
OR - for thousands of rows (with no foriegn key dependencies):
CREATE TEMPORARY TABLE nameTemp( name varchar(128) not null )
INSERT INTO name SELECT name FROM firstTable
TRUNCATE firstTable
INSERT INTO firstTable SELECT name FROM nameTemp
The latter method will only work where you have no foreign keys. If you do, you'll require a lookup table.
CREATE TEMPORARY TABLE lookup( newId INTEGER AUTO_INCREMENT, oldId INTEGER, PRIMARY KEY newId( newId ) );
INSERT INTO lookup (oldId) SELECT id FROM firstTable
[do temp table queries above]
You now have a lookup table with the old to new ids which you can use to update the foreign key dependencies on your other tables (on a test server!)
Changing the primary key is a very bad idea as it endangers your referential integrity (what if another table uses the id without having a foreign key with proper "on change"?).
If you really, really have to do it and don't care about bad side-effects:
Create a second table with identical structure
INSERT INTO new_table (id, [other fields]) SELECT NULL, [other fields] FROM old_table;
DROP old_table;
RENAME new_table old_table;
Warning:
This will damage every other table that has foreign keys on this table (but if you had such then you wouldn't be doing this anyways).
You may want to try something like...
Create Temporary table MyBackup
( ID as your autoincrement,
OldID as Int for backlinking/retention,
RestOfFields as their type )
insert into MyBackup
( OldID
RestOfFields )
select
ID as OldID,
RestOfFields
from
YourOriginalTable
order by
ID (this is your original ID)
Then you'll have a new table with an autoincrement with new IDs assigned, yet have a full copy of their original ID. Then, you can do correlated updates against other tables and set the ID = ID where ID = OldID. By keeping your insert via order by the original ID, it will keep the numbers from replacing out of sequence.. Ex: if your table was orderd as
Old ID = 3, new ID = 1
Old ID = 1, new ID = 3
Old ID = 12, new ID = 2
Your old 3's will become 1's, then the 1's would become 3's, and 12's become 2's
Old ID = 1, new ID = 1
Old ID = 3, new ID = 2
Old ID = 12, new ID = 3
your 3's won't overwrite the higher number, and the 12's won't conflict with the 3's since the threes were already lowered to 2's.