querying records that have revisions related to them - php

I created a commenting system that allow users to submit comments on each item.
It turned into bit of a project/scope creep and now I need to implement the ability for users to edit their original comments and keep track of those comments.
All comments are located in the comments table
comments: id, comment, item_id, timestamp
Now that revisions must be tracked, I created a new table titled revisions:
comment_id, revision_id, timestamp
All comments (new or old) are entered into the comments table, if the user decides to revise an existing comment, it will be entered as a new record in the comments, then recorded into the revisions table. Once the new comment is entered into the comments table, it will take the id that was created and pass it into the revisions.reivison_id, and it will populate revisions.comment_id with the id of the original comment the user revised (hope I didn't lose you).
Now I've come to the problem I need help with: I need to display a list of all comments for a specific item, which would have a query of something like
select * from comments where item_id = 1
Now that I added the revisions table, I need to retrieve a list of comments for the specific item (just like the above query does) and (and heres the kicker) if any comment is revised, I need to return the most recent version of that comment.
What is the best way of accomplishing this?
I thought about running two queries, one to retrieve all the comments in the comments table, store in an array, and another query to return all records within the revisions table where I would set revisions.comment_id to be distinct and would only want to return the more recent one
the revisions query might look something like this
select comment_id DISTINCT, revision_id, timestamp
from revisions order by timestamp desc
What is the best way of only displaying the most recent version of each comment (some will have revisions and most won't)?
I am not a sql expert, so it might be accomplished using sql or will I need to run two different queries, store data into separate arrays, then run thru each array, compare and strip out the older versions of that comment? example (part in theory) below
foreach($revisions as $r):
$comments = strip key/value from comments array where $r['comment_id'] is
found in comments array
endforeach;
return $comments; // return the comments array after it was stripped of the older comments
I imagine if there was a way of running one query to only return a list of the most recent versions of a comment is the best practice, if so, could you provide the appropriate query for that, otherwise is the two queries into two arrays and striping out values from the comments array the best way or a better way?
Thanks in advance.

First off, I'll add two alternative approaches and then I'll edit with a query to deal with your current schema.
Option 1 - Add a deleted flag to your comments. When a comment is revised, do as you already do but also mak the original as deleted. Then you just need WHERE deleted = 0 wher you want active comments.
Option 2 - Change your revision table to be a clone of the comment table, plus an additional field for when the revision was made. Now, whenever you revise a comment, don't create a new record in comment, just update the existign row and add a new row to the revisions table. This is easily maintained with a trigger and is a very standard auditting pattern.
EDIT Option 3 - A query to cope with your schema.
As described, if I make a comment, then edit it twice (with no other activity), I get something like this...
id | comment | item_id | timestamp
----+--------------+---------+-----------
1 | Hello, | 1 | 13:00
2 | World! | 1 | 14:00
3 | Hello, World | 1 | 15:00
comment_id | revision_id | timestamp
-----------+-------------+-----------
1 | 2 | 14:00
2 | 3 | 15:00
Base on this, the live comment is the only one without an entry in the revision table...
SELECT *
FROM comment
WHERE NOT EXISTS (SELECT * FROM revision WHERE comment_id = comment.id)
AND item_id = #item_id

Related

Trying to understand database schema, particular WordPress Comments

I am having trouble fully understanding the schema of the WordPress comments and commentmeta tables, and how they are linked together.
I'd like to learn by making a custom row in each table (wp_commentmeta & wp_comments).
WordPress Database Schema
Following is the example I am working with.
wp_commentmeta:
meta_id | comment_id | meta_key | meta_value
2 1352 verified 1
What does the meta_value denote in the wp_commentmeta table? Is this a rating system 0-5, or similar?
wp_comments
comment_ID | comment_post_ID | misc_cols --- | user_id
2,1352,Waldo,test#test.com,"",127.0.0.1,2014-11-15 00:18:39,2014-11-15 04:18:39,"test comment",0,1,"user_agent","",0,657
comment_type is an empty field, third from last. I'll just tried adding "comment" there, no luck.
The review does show on the backend and the product page however, the product page says "Reviews (0)." The reviews are not being counted on the product page.
Would you please explain this to me?
meta_value in the postmeta table is type-agnostic. What that data represents depends on which plugin/function stored it and what it wants it to mean. You can store integers, dates, strings, or PHP data structures, WordPress does not care, and stores them all as strings internally. In your case, I'm guessing that 1 means the user is verified and 0, NULL or no row means the user isn't verified.
comment_type is similar to post_type. If you want to add a special kind of comment (a review in your case), you'll have to figure out what the software you are using expected as comment_type. Look at existing reviews, what do they have set as comment_type?
In order for the reviews to show the count, I had to navigate to comments, edit the comment, and update the comment (with no changed fields). Perhaps the HTTP server needed to be reloaded, or WooCommerce needs to be reloaded in some way.

Storing a list of songs linked to event mysql

I have a database in MySQL that currently lists approximately 1500 concerts and events. Now, the plan is to add setlists (list of the songs performed at the concerts) for all the concerts in the database. Basically this will mean a lot of repeated values (songs performed at many concerts), and I would really appriciate some input on what the best approach would be.
I initially started out with a database similar to this;
| eventID | edate | venue | city | setlist |
The field setlist was basically text data, where I could paste the list of songs and parse through it to put each song on a new line with php. This works, and editing the text and running order was like editing a text document. Now, obviously this was pretty simple, but has drawbacks and limitations. Simple things like getting stats on songs performed is probably very difficult, right?
So, what is the best way to store the setlist value?
Create a new table that adds a new row for each song performed, and that has a foreign key linking to eventID? How would I best retain (and edit, if needed) the running order of the songs in that table? Any other suggestions?
Thanks for any input or advice on this, as I would love to get some help before I start adding all the data.
I would create a table that holds each song performed at a specific event:
| songId | eventID | song |
Where eventID can be duplicated in multiple rows to show each song performed at that event.
This way you can query all the times a specific song was performed, and also get all songs (the setlist) for a specific event by querying on the eventID.

Article with revisions system

For a project I am making I need the possibility (like stackoverflow does) to save all the previous edit (revisions) for posts.
Consider I can have some 1 to N association with the post (for example 1 post with 5 images associated).
How would you suggest me to design the database for this?
Of course the ID of the post should stay the same to don't broke URLs:
site/post/123 (whenever revisions it is)
Each revisions to posts should be manually approved so you can't show directly the last revisions inserted. How would you suggest me to design the db?
I have tought
Table: Post
postID | reviewID | isApproved | authorID | text
And the image table (for example image, but it could be everything)
Secondary Table: Image
imageID | postID | reviewID | imagedata
Actually, I would split the post table in two, with the approved revisions in one, and the latest (not approved) revision in another. The rational is that any non approved revision which is not the latest would be supersceded by the next one (unless you really want to keep track of all the intermediate modifications, approved or not).
Table: OldPost
postID | reviewID | authorID | text
Table: PendingPost
postID | authorID | text
In that layout, whenever a new revision has been approved, it must be moved to the approved ones, but you don't have to filter them out when displaying the whole history, and conversely, you wont have to filter the approved revisions in the approval part of your site.
You could even refine the layout with yet another dedicated table for the latest approved revision (so three tables for the post in total, not counting attachements). This partitioning would improve the overall performance of your site for the most common queries, at the cost of more complex queries when you need all the data (less frequent operations).
Table: CurrentPost
postID | authorID | text
As you can see, this table structure is the same as the one for pending posts, so the updates would be trivial.
moving a revision to the old post table requires to find out the revision count, but you would have to do that operation anyway with a more classic db layout.
Regarding the attachment table, the layout seems to work.
Separate all aspects of a post between global information and versionable information. In other words, what things can be changed in a revision and what are always going to apply to any revision. These are going to be the fields in your two tables, one for your posts, and one for the revisions. You will also need a row to specify what post the revision is for as well as whether the revision is approved, and on the posts table, you need a row to specify what the current revision in.

Have an array in a SQL field. How to display it systematically?

I have a field in a data feed coming in with some values separated by commas. For one record, the values are:
A06,C05,C06,C15,C18,C19,C21,C22,E05,E22,G11,J02,J07,L04,L07,M01,M05,N03,N07,N10,N11,N12,N18,N19,N20,N24,O02,O03,O04,O06,O09,O14,O15,O16,O20,O21,O31,Q01,Q04,Q08,R07,S08,T08,T12,T23,T32,U01,U03,U04,U06,U13,W09,W11,W16,W19,W30,W45,X02,X03,X12,Z07
I have a separate table with some descriptions as to what each code means. When I query the main table and get this field name as a value, I can use explode to get it into an array and use a foreach loop to output each value.
The problem is, I want to display the description stored in another table. What's the proper way of iterating through this to display these values in a list?
As an example, C21 means "Gated Community."
You can use FIND_IN_SET() function for that.
Example you have record like this
Orders Table
------------------------------------
OrderID | attachedCompanyIDs
------------------------------------
1 1,2,3 -- comma separated values
2 2,4
and
Company Table
--------------------------------------
CompanyID | name
--------------------------------------
1 Company 1
2 Another Company
3 StackOverflow
4 Nothing
Using the function
SELECT name
FROM orders, company
WHERE orderID = 1 AND FIND_IN_SET(companyID, attachedCompanyIDs)
will result
name
---------------
Company 1
Another Company
StackOverflow
As you have tagged codeigniter you could use the built in Active Record's method $this->db->where_in(); to get the description. For example consider the code below
$codes = array('A06', 'C05', 'C06');
$this->db->where_in('description', $codes);
// Produces: WHERE codes IN ('A06', 'C05', 'C06')
For more information about Active Records of Codeigniter refer Active Record Class
For more information on how mysql WHERE IN works refer Tutorial
Just for follow the books, the best way of doing this in SQL language is to use the relationship.
For understand this I recommend you read this simple paper http://net.tutsplus.com/tutorials/databases/sql-for-beginners-part-3-database-relationships/
and maybe this http://www.informit.com/articles/article.aspx?p=30875&seqNum=5 for SELECT the data, or search for yourself on the web, and I recommend you to try yourself the examples. SQL Relationship Is good and necessary for security and many others reasons.

Concurrent mutiuser access php+mysql

Im going to develop Stock maintaining system using php+mysql. which will runs on server machine, so many users can update stock data. (in/out)
Im currently working on this system. I have following problems.
User A opens record “A”. ex- val=10
User B opens record “A”. ex - val=10
User A saves changes to record “A”. ex - val=10+2=12 (add 3 items, then stock should be 12)
User B saves changes to record “A”. ex - here i need to get record "A" value AS = 12, then B update val=12+3=15. (then add 3 items final stock will be 15)
In this example, User A’s changes are lost – replaced by User B’s changes.
I know mysql Innodb facilitate row level locking. My question is ,
is innodb engine do concurrent control ; and is this enough to (Innodb) to avoid "lost update" problem. or need to do extra coding to avoid this problem.
Is this enough please tell me how innodb works with my previous example. (lost update)
(sorry for my bad english)
thanks
InnoDB allows concurrent access, so User A and User B could definitely be handling the same data. User A will update the row based on his/her data, then User B can do the same -- ultimately resulting in User A's loss of data.
You should consider an alternative, if every update is vital to keep. For example, if both users are updating a blog article, you could make a new table that holds all these edits. Both user's edits would be preserved, despite when they retrieved the article content. When the article is retrieved, you can check when the most recent edit occurred and retrieve that instead.
Look, there's something called "versioning".
The idea is simple:
When a user opens a record, he also gets the version number.
When he saves changes to that record, at the sql level, the update is conditional, meaning that the update will happen ONLY if the current version is the same. This update also increases the version by one.
This way ensures you're not writing to a "stale" copy of your record.
Hope it's clear.
You could also implement some polling to the server, keep a record of the last update of the row and if it changes where if user B updates the record before A then you can notify user A that the record has been updated and that his changes wont take effect or you could update the values dynamically.
You can use two tables for this purpose. First - StockItems with item name, id, and count. Second - StockActivities with item id and operation amount.
To add or remove items from stock you need to insert records to the second table StockActivities, with item id and quantity that is added / removed.
item id:1, qnt: +10
item id:1, qnt: +1
item id:10, qnt: -2
Field count of StockItems table should be "read only" for users and should be calculated based on StockActivities table.
For example, you can create after insert trigger for StockActivities table that will update count field of added / removed stock item.
Judging by comments left, I think it prudent to respond with some pointers I have come across, in case someone needs to.
If you only want to update a value by an offset, you can do this quite easily and atomically. Assume the following data:
+----+--------+-------+
| id | name | price |
+----+--------+-------+
| 1 | Foo | 49 |
| 2 | Bar | 532 |
| 3 | Foobar | 24 |
+----+--------+-------+
We can now run the following queries to add one to the price:
select id, price from prices where name like "Foo";
// Later in the application
update prices set price=50 where id=1;
This is the non-concurrent/non-atomic way to do this, assuming that there is no changes or fetches in between the two queries. A more atomic way to do this, is the following.
select id, price from prices where name like "Foo";
// Later in the application
update prices set price=price+1 where id=1;
Here, this query allows us to increment the price in one query, eliminating the ability for others to come and update between two queries.
Additionally, there are methods of updating data safely, where the nature of the update is not a simple addition or subtraction. Let's say, here, that we have the following data:
+----+----------+---------------------+
| id | job_name | last_run |
+----+----------+---------------------+
| 1 | foo_job | 2016-07-13 00:00:00 |
| 2 | bar_job | 2016-07-14 00:00:00 |
+----+----------+---------------------+
In this case, we have multiple different clients, where all clients can do any job. We then need a way to dispatch work to one client, and only one client.
We can either use a transaction, where we will error out if the record has been updated or we can use a technique called CAS, or Compare and Swap.
Here's how we do this in MySQL:
update jobs set last_run=NOW() where id=1 and last_run='2016-07-13 00:00:00'
Then, in the data returned from mysql, we can tell the number of rows affected. If we have affected a row, then we have successfully updated it, and the job is ours. If there were no rows updated, then another machine has updated it, claiming the job there.
This works because any update from our application will cause the column to change, and since the column's value is a condition for completing the updated, it will avoid concurrent changes, allowing the application to decide what occurs next.

Categories