Multiple insert queries - php

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.

Related

Add second (conditional) result from second table to SQL query

I have two tables in a database. One stores names/details of users with an index ID; the other stores articles they have written, which just keeps the user's ID as a reference (field author). So far so simple. I can easily query a list of articles and include in the query a request for the user's name and status:
SELECT a.name, a.status, s.* FROM articles s, author a WHERE s.author=a.id
The problem comes when I occasionally have a second author credit, referenced in field author2. Up till now I've been doing what I assume is a very inefficient second query when I iterate through the results, just to get the second author's name and status from the table (pseudocode):
while ( fetch a row ) {
if (author2 != 0) {
query("SELECT name, status FROM author WHERE id=author2") }
etc. }
While this worked fine in PHP/MySQL (even if clunky), I'm forced to upgrade to PHP7/PDO and I'd like to get the benefits of unbuffered queries, so this nested query won't work. Obviously one simple solution would be to PDO->fetchALL() the entire results first before iterating all the result rows in a foreach loop and doing these extra queries per row.
But it would be far more efficient to get that second bit of data somehow incorporated into the main query, pulling from the author table using the second ID (author2) as well as the main ID, so that there are name2 and status2 fields added to each row. I just cannot see how to do it...
It should be noted that while the primary author ID field is ALWAYS non-zero, the author2 field will contain zero if there is no second ID, and there is NO author ID 0 in the author table, so any solution would need to handle an author2 ID of 0 by providing null strings or something in those fields, rather than giving an error. (Or far less elegantly, a dummy author ID 0 with null data could be added to the author table, I suppose.)
Can anyone suggest a revised original query that can avoid such secondary queries?
Never use commas in the FROM clause. Always use proper, explicit, standard JOIN syntax.
For your query, use LEFT JOIN:
SELECT s.*, a1.name, a1.status, a2.name, a2.status
FROM articles s LEFT JOIN
author a1
ON s.author = a1.id LEFT JOIN
author a2
ON s.author2 = a2.id
Gordon Linoff's answer looks like what you need.
I would have added this as a comment but it is too long of a message...
I just have a question/comment regarding normalization of the database. Would there ever be an instance when there is an author3? If so then you should probably have an ArticleAuthor table. Since you are rebuilding the code anyway this may be an improvement to consider.
I don't know the names and data types of the information you are storing so this is a primitive example of the structure I would suggest.
Table Article
ArticleID
ArticleData...
Table Author
AuthorID
AuthorName
AuthorStatus
Table ArticleAuthor
ArticleID
AuthorID
If the Status is dependent on the Author Article combination then AuthorStatus would be moved to ArticleAuthor table like this.
Table ArticleAuthor
ArticleID
AuthorID
Status

Wallposts and comments

I've made one table for all comments on a social network site: comment
Also, I've one table for all comments assigned to one comment: comment_assign
So, I built a function comment() to implent it easily in each section type (images, userpage, groups, etc). In case of $_GET['s']==user, I want to have wallposts as well as comments on these wallposts. All stored in 'comment'.
I've got this scheme to display this:
1. sql query to get the comments
2. html output
3. another sql query inside this html output to get specified assigned comments of a comment (wallpost in this case)
Now the problem is that my first query displays all comments. Also comments that are supposed to be subcomments. So my question is, if there's any way to specify in this first query, when I get all my comments, to say: Look in comment_assign if this comment_id is available. And if it is, don't display this comment, because it's a subcomment (that I'll display in mentioned step 3).
Maybe this whole structure may be changed? I would appreciate any suggestions. Even hard to realized ones, but which would be the most efficient.
Table structure:
comment
id, uid, nid, site, text, date
comment_assign
comment_id, assign_id
First SQL Query example, which doesnt work to avoid displaying all the comments (also assigned ones). See the last line:
SELECT *
FROM `comments` AS c
LEFT JOIN `comment_assign` AS ca ON ca.`comment_id` = c.`id`
LEFT JOIN `users` AS u ON c.`uid` = u.`id`
WHERE c.`nid`='".$nid."'
AND c.`site`='".$_GET['s']."'
AND ca.`comment_id` != c.`id`
If I understand you correctly, you select all the comments from the comment table. You then want to check to see if comment.id is present in comment_assign.comment_id. If it is present, it is a sub-comment. Is that correct?
You can do it two ways - the clean way is to add another field to the comment table and put assign_id there, since each comment can only be associated with another comment, or is a top-level comment (*assign_id is NULL*).
Alternatively, you could LEFT JOIN both tables. Every row where assign_id is NULL, is a wall comment, every row where it has a value means it is assigned as a sub-comment. i.e.
SELECT id, uid, site, text, date
FROM comment
LEFT JOIN comment_assign ON (comment.id = comment_assign.comment_id)
WHERE comment_assign.assign_id IS NULL;

mysql DELETE with field check from another table

Good morning all, happy thursday morning. I wish I could have done this by myself but since I'm not a master in MySQL statements (yet) and I got lost in this DELETE query, here it goes...
I have to do a simple DELETE query like this, (deleting a comment by its id)
DELETE FROM mya_news_comments WHERE comment_id='".$_GET['comment_id']."'";
but at the same time, to prevent people deleting comments throughout the website
i need to ensure that the person deleting this comment is who it is supposed to be (in our case, an artist).
I have another table mya_news which has among the fields news_id, artist_id
In mya_news_comments I also have a field called news_id
So I need to check that I delete the comment_id of the particular artist, not of other artist.
Basically i need to cross-check if the news_id field from mya_news_comments checks out with a field with same news_id from mya_news, and artist_id from mya_news is equal to $_id (which holds my artist_id)
I'm really stuck here. I'd be glad to give more details if needed.
Thanks.
DELETE mya_news_comments
FROM mya_news_comments
WHERE mya_news_comments.comment_id = (SELECT [another_table].comment_id WHERE [cond])
AND mya_news.artist_id = (SELECT [another_table].artist_id WHERE [cond]);
You can join tables in DELETE statements just like you can in SELECT statements:
DELETE mya_news_comments
FROM mya_news_comments JOIN mya_news USING (news_id)
WHERE mya_news_comments.comment_id = ?
AND mya_news.artist_id = ?

Updating a many-to-many connector table all at once

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());

Make MySQL table unique

Hay, I created a spider to crawl through a PDF document and log every word in the document into a table in a MySQL database.
Obviously words like 'the', 'and', 'or' etc appear in a book many, many times.
I'm just wondering what's the quickest method to remove dupe values from a table?
Create a table without indexing the words and put in all the words from the book using mass inserts (you could also use LOAD DATA). When you're done with insertions, add a new Index on the word field
Then create a second table using:
CREATE TABLE newTable SELECT DISTINCT word FROM oldTable
Instead of removing duplicates, you could make sure that no duplicates ever make it into the table.
Presuming your table has only 2 fields, id and word:
INSERT INTO table SELECT null, 'word' FROM table WHERE NOT EXISTS (SELECT * FROM table WHERE word = 'word') LIMIT 1;
This will insert the word into the table only if it's not already in there
If you can rerun the script to populate the database, you could add a unique key on the "word" field and instead of INSERT INTO do a REPLACE INTO. This will delete the previous instance of the record before adding a duplicate field. This may not be the most efficient way to do it, but it's rather simple. See here for more details:
http://dev.mysql.com/doc/refman/5.0/en/replace.html
select distinct on word field, and then delete all rows that have a different id? I'm not a master in subqueries so no example atm :)
delete from words where idcolumn not in
(select min(idcolumn)
from words T2
where T2.plain = WordsTable.plain)
This works if you added (idcolumn, plain) for every word you found.
If you do not have an id column (pk) then you can use Anax's solution.
In addition to not inserting duplicates (codeburger comment), you can just set a unique index on your plain column.

Categories