So my database's primary key is just a column called 'id'. When i go to add a new item into the database I want it in a specific order without having to go into the DB and changing every value after it to +1 whatever it was before.
Example:
ID
| 1 | item1 |
| 2 | item2 |
| 3 | item3 |
| 4 | item4 |
Say I want to add an item inbetween item2 and item3. To do that I would need to change item 3's id to 4 and item4's id to 5, but currently I have to go into the database and do it automatically.
How would I make it increment automatically when I INSERT a new item?
You should consider leaving IDs unaltered and adding a secondary column to sort by, e.g. sort_order. Needing to alter all the IDs in the parent and related tables for every insert can't be a good idea, esp. if you don't have properly crafted foreign keys.
If you do so, it should be fairly easy to accomplish:
-- Untested
START TRANSACTION;
UPDATE foo SET sort_order=sort_order+1 WHERE sort_order>=4;
INSERT INTO foo (name, sort_order) VALUES ('item', 4);
COMMIT;
Related
I want to create a small copy of a bigger table and link both of them.
Every time I make an update in the bigger one, I want that the small one updates too.
For example, I have this data:
Big table:
id | name | price
1 | a | 10
2 | b | 12
Small table:
id | name
1 | a
2 | b
---- UPDATING THE BIGGER ONE ---
Big table:
id | name | price
1 | y | 10
2 | b | 12
3 | c | 13
Small table should become AUTOMATICALLY (after I update the bigger one):
id | name
1 | y
2 | b
3 | c
Do you know how to do it?
To do exactly what you're asking you could use triggers. Triggers are SQL that execute automatically when certain events happen. To mirror the data you would need to create UPDATE, INSERT, and DELETE triggers. (I don't have access to a MySQL instance at the moment to test, so there could be typos)
CREATE TRIGGER big_to_small_insert
AFTER INSERT ON big
FOR EACH ROW
INSERT INTO small (id, name) VALUES (NEW.id, NEW.name)
CREATE TRIGGER big_to_small_update
AFTER UPDATE ON big
FOR EACH ROW
UPDATE small SET name = NEW.name WHERE id = NEW.id
CREATE TRIGGER big_to_small_delete
AFTER DELETE ON big
FOR EACH ROW
DELETE FROM small WHERE id = OLD.id
However, a View is probably a better option if the "small" table is truly just the big table with a subset of data. A View won't store a copy of the data, so if you update the table (big) it will be reflected in the view (small), but the opposite is also true. If you do an INSERT, UPDATE, or DELETE on the view (small) it will actually happen in the table (big).
CREATE VIEW small AS
SELECT id, name FROM big
That might sound weird, I know. This is an explanation:
1- I have the following table - items (which gets updated because users can update the amount of items as well as the content inside of them):
| id | content | item_id | Order (Unique Index)|
|:-----------|------------:|:------------:|:--------------------:
| 1 | This | 1 | 1
| 2 | is | 1 | 2
| 3 | content | 1 | 3
| 4 | Some | 2 | 1
| 5 | More | 2 | 2
| 6 | More! | 3 | 1
2- On my server, I am running a query that will iterate through my POSTed content check every item based on its item_id as well as to check if the order in that row is set. If order is set, then update the content, else insert new content
Lets say that my content is POSTing 4 items and the item_id = 1. Preferably, what I would want it to do would be this:
| id | content | item_id | Order (Unique Index)|
|:-----------|------------:|:------------:|:--------------------:
| 1 | This | 1 | 1
| 2 | updated | 1 | 2
| 3 | content | 1 | 3
| 4 | Some | 2 | 1
| 5 | More | 2 | 2
| 6 | More! | 3 | 1
> 7 | added | 1 | 4
Note that what happened was, it added a new row because my POSTed content had four items in it. It iterated though every single one, checked if the order existed, if the order existed, then update the value, else create a new row and insert the value as well as the order (key). The order is pretty much the key. That's how I am setting it in there.
It doesn't work when I do this:
// Start loop - for (key in content) {
INSERT INTO items (item_id, content, content_order) VALUES (?, content[key], ?) WHERE item_id = ? ON DUPLICATE KEY UPDATE content = ?
// End loop
What the loop is doing, it is iterating through every content POSTed and inserting it into the database, and if there is a duplicate key (in this case, the Unique Index is the Order column) then only update the content inside of it.
The problem with this is, it will only work on the first three items. Why? Because the first three items are the first ones with those unique indexes. If I was to update the item in which the item_id is 2, then it would give me an error because I cannot update items that have the same unique key. I cannot even INSERT anything because it violates the Unique Index constraints!
So how can I do this?
Is there a way to make the Unique Index absolute to the query - meaning that it will only keep in mind the Unique Indexes based on the queries' specified item_id? (Doubt it)
How can I make it so that it checks if the order is set and update the content or insert a new row without using unique keys?
Is there an alternate way to write this?
If elaboration is needed, please let me know. Thanks.
A straightforward design for your needs would probably have no problems. Although your question is unclear, especially about new content.
Order is not a key of items. Because column order is not unique. The key you want is (item_id,order).
Do you need items id? I'm going to ignore it. I'm going to treat new content as if it were in a table. You probably should build a constant subquery from it. Do all new content item_ids appear in items?
1. No NULLs.
A simple design is to have a version of items called content that holds the rows that make the following fill-in-the-blanks statement true. I'll assume items order is consecutive within item_id.
// item [item_id]'s [order]th content is [content]
content(item_id,order,content)
primary key (item_id,order)
I will guess at your new content format and effect. I'll assume order is consecutive from 1 within item_id. I'll replace all content info for a new content item_id.
// item [item_id]'s [order]th content is [content]
more_content(item_id,order,content)
primary key (item_id,order)
delete from content
where item_id in (select item_id from more_content)
insert into content
select * from more_content
2. NULLs
If NULL order indicates that there is no content then you can instead have content NULL and order=1. (You can also have another table and no NULLs.) If NULL order indicates that there is an unchanged default then just have another table:
// item [item_id]'s content is a default
has_default(item_id)
primary key (item_id)
delete from has_default
where item_id in (select item_id from more_content)
delete from content
where item_id in (select item_id from more_content)
insert into content
select * from more_content
If you want items readable the way it is, make a view:
// [order] is null AND item [item_id]'s default content is [content]
// OR [order] is not null AND item [item_id]'s [order]th content is [content]
create view items as (
select c.item_id,c.content,if(d.item_id is null,c.ord,NULL) as ord
from content c left join has_default d on c.item_id=d.item_id
)
It's hard to make out much more about your design.
It may be difficult to implement constraints for any design for your needs in SQL. But you should start with a straightforward design.
Lookup table - unique row identity
The other lookup tables just do not make sense as from what I have seen giving a row an ID then putting that id in another table which also has a id then adding these id's to some more tables which may reference them and still creating a lookup tables with more id's (this is how all the examples I can find seem) What I have done is this :
product_item - table
------------------------------------------
id | title | supplier | price
1 | title11 | suuplier1 | price1
etc.
it then goes on to include more items (sure you get it)
product_feature - table
--------------------------
id | title | iskeyfeature
1 | feature1 | true
feature_desc - table
-----------------------------
id | title | desc
1 | desc1 | text description
product_lookup - table
item_id | feature_id | feature_desc
1 | 1 | 1
1 | 2 | 2
1 | 3 | 3
1 |64 | 15
(as these only need to be referenced in the lookup the id's can be multiples per item or multiple items per feature)
What I want to do without adding item_id to every feature row or description row is retrieve only the columns from the multiple tables where their id is referenced in the same row of the lookup table. I want to know if it is possible to select all the referenced columns from the lookup row if I only know the item_id eg. Item_id = 1 return all rows where item_id = 1 with the columns referenced in the same row. Every item can have multiple features and also every feature could be attached to multiple items , this will not matter if I can just get the pattern right in how to construct this query from a single known value.
Any assistance or just some direction will be greatly appreciated. I'm using phpmyadmin, and sure this will be easier with some php voodoo I am learning mysql from tutorials ect and would like to know how to do it with sql directly.
Having a NULL value in a column is not the major concern that would lead to this design - it's the problem with adding new attribute columns in the future, at which MySQL is disgracefully bad.
If you want to make a query that returns everything about an item in one row, you need to LEFT OUTER JOIN back to the product_lookup table for each feature_id. This is about every 10th mysql question on Stack Overflow, so you should be able to find tons of examples.
I've got a table in MySQL that has a Primary Key Column.
Lets say:
ID | Value
1 | One
2 | Two
6 | Three
8 | Four
9 | Five
How do I get it to be:
ID | Value
1 | One
2 | Two
3 | Three
4 | Four
5 | Five
There are no other tables. Just the one.
I just want the ID to be in a proper series.
Any suggestion??
A Query perhaps.. :)
There is even a simple way to accomplish the result by writing this query
SET #newid=0;
UPDATE tablename SET primary_key_id=(#newid:=#newid+1) ORDER BY primary_key_id;
This query will reindex the primary key starts from 1
Seems to me you have two options.
1) create a new table and copy the existing data over.
2) add another autoincrement field to the existing table, then delete the original column.
ALTER TABLE tableName ADD NewIdn INT NOT NULL AUTO_INCREMENT KEY
I did this in phpmyadmin by unchecking the A_I box (Auto Increment setting), clicking save, and then checking it again, and clicking save again.
I have a table like the following,
| id | name | color |
------+--------+---------
| 1 | pear | green |
| 2 | apple | red |
| 3 | banana | yellow |
| 4 | grape | purple |
I'd like to reorder alphabetically using the "name" column and reset the id (autoincrement) with this new order to end up with the following
| id | name | color |
------+--------+---------
| 1 | apple | red |
| 2 | banana | yellow |
| 3 | grape | purple |
| 4 | pear | green |
QUESTION: how can I do this with MYSQL?
The cleanest way to reset the auto increment is to create another table.
MySQL provides commands such as CREATE TABLE LIKE and RENAME TABLE that are useful.
CREATE TABLE table2 LIKE table1;
INSERT INTO table2
SELECT * FROM table1 ORDER BY name;
DROP TABLE table1;
RENAME TABLE table2 TO table1;
Can I ask why you would want to do this?
If anyone modifies any of the name values or inserts new rows it will mess up your ordering scheme. Trying to store some meaning in the ordering of the PK that is already available elsewhere in the table (the name column) seems redundant and consequently a bad idea.
A much better solution is not to worry about the value of the ID column and just sort on the name column when you use the data in your app.
PS: Sorry for the non-answer type response. Normally I'd assume you had a good reason and just give an answer that directly addresses what you are trying to do, but I noticed from your other questions that you are still in the early learning stages about database design, so I wanted to help point you in the right direction instead of helping further your progress towards an ill-advised approach.
You can SELECT INTO a new table from the old table, ordering your select into as desired. Have an auto-increment ID in the new table. If needed, drop the old table and rename the new table.
Why not adding "ORDER BY name ASC" at the end of your query? My guess would be that you need the ID for some reason.
If you have a table with an autoincrement primary key (named 'id' for example), and the key is not being depended on by other tables, the way to go about this is this.
Remove the column id entirely.
alter table order by column_x, column_y;
Add the primary key column 'id' again, with autoincrement.
I did this a few times, with success and quite fast, using phpmyadmin.
Reordering a table that has a primary key as an index, is impossible. That's why u need to remove it first.
If u need to keep the 'id' column, but need to re-sort, based on other columns, u need to omit the primary key & index status of the id column & re-sort. Then you need to add a new column as primary key / index.
SELECT
RANK() Over (ORDER BY Name) As NewID
, Name
, Color
FROM Fruits
could save to a temp table then truncate then truncate the fruit table and insert, but it's probably a crappy solutions.