Say I have two tables:
articles
categories
There's a many to many table that connects them.
CREATE TABLE cat2art (
article_id INT,
category_id INT
);
For a specific article, I have a 'new list' of category id's, and we need to update the cat2art table with these.
Some categories got removed, some got added and some stayed where they were. What is the most effective way to update this table?
I could naively delete all records with the specified article_id and simply add them again. However, if I were to record a date in that same table that tracks when an article was linked to a category, that information will now be destroyed.
I'm looking for a great pattern that easily solves this issue. This question is specifically for PHP and MySQL, but answers in other languages are also fine provided they are applicable to PHP+MySQL as well.
Other systems support MERGE statement which would do exactly what you want.
However, in MySQL, you would need at least two queries (it cannot delete and insert/update in a single statement):
DELETE
FROM cat2art
WHERE art_id = $art_id
AND cat_id NOT IN ($new_cat_1, $new_cat_2, …);
INSERT
IGNORE
INTO cat2art
VALUES
($art_id, $new_cat_1),
($art_id, $new_cat_2),
…;
You can define (article_id, category_id) as unique key, and when inserting a connection use INSERT IGNORE syntax. That way if this connection already exists it will not be added again, nor will it update the existing record, and the create_date column stays untouched.
example:
INSERT IGNORE INTO cat2art (article_id, category_id, create_date)
VALUES(100,200,NOW());
Related
it's late, my child cried all dayand I'm expected to pull this off by the next 3 hours.
I have a 3 MySQL tables "entities", "users", "priviledges" (all have their id as key).
The table "priviledges" is having these columns: id(PRIMARY KEY), user_id, entity_id, priviledge
The thing is that when...
I grant the highest priviledge(3) to a user the system should create new table entries for all remaining entities
(once you become "admin" for one entity, you should be "admin" for all entities; if you should have priviledge(2) for some entity already, it should be changed to 3).
I cannot alter the SQL structure and I'm using PHP with PDO. I tried, but I always end up with duplicate entries in "priviledges" as I'm not able to utilise INSERT IGNORE (yep, still SQL noob).
Right now I just select all users with priviledges.priviledge(3), then I select all DISTINCT entities.id and dump them into PHP arrays and then I loop foreach through every "user" and "entity". So instead of sending 1 query, I'm ending with USERSxENTITIES and rightnow its over 500 queries.
Will there be a merciful soul to help me?
First it seems from your data structure that (user_id, entity_id) is meant to be unique.
Because of your "no table schema changes" criteria (otherwise I'd drop id (assumed to be an auto_increment) and add this as the primary key), so instead, create a unique index:
CREATE UNIQUE INDEX uniq_u_e ON priviledges (user_id, entity_id)
You probably have duplicate already so search existing questions for deleting duplicates and then add this index.
I assume you have another table of entities.
To create an admin (3) user on all entities that has an id as its entity primary key.
INSERT INTO priviledges( user_id, entity_id, priviledge)
SELECT 42 as user_id, id, 3
FROM entities
ON DUPLICATE KEY
UPDATE priviledge = 3
So this ensure that the user 42 has admin(3) access on all entities.
I am trying to SELECT id, description, title FROM table1, table2, table100
Say I get this working, is it better for me to just combine all my tables in phpmyadmin?
The problem is I have around 100 tables all of different categories of books so I want to keep them seperated in their individual tables.
I am trying to make a search engine that searches all the books in the entire database. All tables have the same column names.
So really all I really am trying to do is search the entire database's tables for an id, description, title. My search works, just I can only search 1 table and every solution online I have found only really works efficiantly with 2 or 3 tables.
Thanks in advance.
The best is to redesign your database, everything into a single table with an additional "category" column.
in the meantime, you can create a view which union the tables with an additional column for the category.
I recommend redesign the model and unifique this 100 tables to 1, and add a new column with category but integer value, not string value. In this way, you can index the category column with the other fields (id, description, title) for speed up the query.
This resolution is more easy for avoid pain later.
I recommend keeping one table A with id, description, title, category and create another table B with categories. Table A has to have a foreign key with table categories. Then create a query to retrieve the books with a specific category.
Example:
SELECT id, description, title, category FROM books WHERE category = "drama"
I think it speaks to the database design itself as mentioned by most here. You've a few options depending on how much time you have on your hands:
(Short Term / Quick Fix) Central table with all your current fields plus category as a flag to differentiate between the current tables you have. So your insert will be something like "INSERT INTO newtable (ID,AssetID,ServiceID,Category) SELECT id, description, title, 'Fiction' FROM table1 ;"
If you tables are incrementally named like table1, table2 upto table100, you could then maybe write a quick php script that will iterate through the insert loop while incrementing on table on each iteration until the last table.
In the long run, you could invest in a json field that will house all your other data excluding keys that pertaining to a single entry
I have a product_group table with the following fields: group_id, product_id, order. The table will be queried against a lot: a single-form view will make it possible to insert new records and/or update existing ones with one submit.
I'm trying to figure out optimal solution to cover the following 3 cases:
User tries to insert an existing row: do nothing. Here a unique index of the 3 columns can be useful.
User changes only the order column: perform an update.
User inserts a completely new set of values: perform an insert.
Is there a way to put all of this together in one MySQL query? If not, what would be the best approach here? The goal is to limit database queries as much as possible.
Does this do what you want?
insert into product_group(group_id, product_id, `order`)
values (#group_id, #product_id, #order)
on duplicate key update `order` = values(`order`);
Along with a unique index on group_id, product_id:
create unique index idx_product_group_2 on product_group(group_id, product_id)
This handles your three cases:
Because the value assignment is a no-op if the values are the same.
The order column will be updated if the other two have the same value.
A new row that has a different group_id or product_id will be inserted.
As a note, order is a lousy name for a column, because it is a SQL key word.
I have 2 tables that have one-to-one relationship in mySQL. they both have the same user_id as Primary key, and I need somehow when I insert a post into the my first table, automatically be able to insert the row with same user_id to my second table. Is there any mysql command or PHP script that I can use it ?
You might set up a TRIGGER in the database. These trigger entities are stored in the database's structure, with PHP you might only execute the CREATE TRIGGER query which creates one.
However, two tables having the exact same data as their PRIMARY KEY sounds like your database structure is a bit badly modelled. You should take time to remodel the database, essentially merging (if possible) the two tables together.
And if you are using PHP to INSERT INTO the database, you can call the two queries after each other:
INSERT INTO table1(field1, field2...) VALUES (value1, value2...)
INSERT INTO table2(field1, field2...) VALUES (value1, value2...)
But reliance on two queries after each other requires pinpoint accuracy as the primary keys might go out of sync, breaking the relations.
If I understand your question right you can get the inserted user_id from the first table and use that to insert a new row in the second table.
$query = mysql_query("SELECT * FROM first_table ORDER BY user_id DESC LIMIT 1");
$row = mysql_fetch_assoc($query);
Now $row['user_id'] can be inserted in the second table.
This could of course be risky if many rows are inserted at the same time.
I have three tables that essentially cascade down, like:
topic
section (references topic id)
subsection (references topic id and section id)
Whats the best method of writing mysql statements to create the initial topic/section/subsection so I can grab the id's (auto_incremented) of the newly created rows and use them to insert them into the second two?
edit
I'm using phpbb3, dunno if that makes a huge difference, but I normally use the $db-sql_query() function
From the parent, down the line.
Then you can use either LAST_INSERT_ID(), or INSERT in the SELECT:
INSERT INTO TOPIC
(topic_id, topic)
VALUES (DEFAULT, $topic);
INSERT INTO SECTION
(topic_id, section)
SELECT topic_id, $section
FROM TOPIC
WHERE topic = $topic
INSERT INTO SUBSECTION
(section_id, topic_id, subsection)
SELECT section_id, topic_id
FROM SECTION
WHERE section = $section
This example assumes that TOPIC.topic_id, SECTION.section_id, and SUBSECTION are auto_increment, primary key columns.
You can use mysql_insert_id() to get the last insert ID. see reference for more details.
As far as I understand, there's only one way, which is top-down.
To add a subsection to a section, you must have had a section already inserted.
To add a topic to a subsection, make sure you added the subsection first.
On the way, you keep track of the last inserted ID (if you just created it) to give it to the next entity down the line.