Ordering items in database - mysql

I'm searching for an efficient way to add order the inserted item in a table in a database.
I'm thinking of two ways to strategies to achieve it.
First strategy:
to add order column in the table, and maintain its consistency with every Create, Update, and Delete (its very costly method) especially when I need to change the order, I need to change all elements after the updated element.
item order
1 2
2 1
3 4
4 3
Second strategy:
to make a meta_data table for all metadata and save the order and id of the product.
for example:
'products' => [{id:1, order:2}, {id:2:order:1},......]
I can replace the metadata list with each modification, but it is also a costly method when selecting data from a table.
Are there any more efficient way.

If you are insistent on ordering correctly after every edit, create a view like so:
CREATE VIEW ViewerData AS
SELECT * FROM Products
ORDER BY Id
Then use the view as your back end table, whilst you are still able to update the actual table - by using the view instead of the table, it will automatically reorder every time you requery.

Related

Drag and drop list, best performance to save the order from the elements in mysql

i'm working on a draggable list, we need to save the order or priority from the elements, but i have problem with the performance.
We have three types of elements, buttons, images and videos, the user can add elements and reorder.
My question is, how can i have the best performance, imagine that in the future we can have 100 000 pages, each page with 15 elements between images, videos and buttons and the database should be updated every movement with ajax?
First option: Creating 4 tables, images, videos, buttons and priority, having a relationship between them and their priority.
First option
Second option: Create only one table called "elements" with an attribute called priority and three attributes, "is_video", "is_image", "is_button".
Second option
Probably having a single table with the order of items will be the best because you operate on a single table for all reordering operations. Something like that can work well:
SORTED_ITEMS
--------------
PAGE VARCHAR -- Identifier of the page
ID_ITEM VARCHAR -- Identifier of the item
POSITION INT -- the position of the item inside the page
Than you can have an ITEMS table holding all the data that probably don't change during a reordering.
ITEMS
-----------------
ID_ITEM VARCHAR PK - THe primary key of the item
TYPE VARCHAR - The type of the item (video, image, button...)
... - All other fields related to an item
This solution perform well because the sorting operation doesn't affect a big table and gives you the ability to add new kind of items in the future without changing the code for sorting them.
If the number of items per page is limited to a reasonable number you can also create a table for PAGES like the following
PAGES
------
ID_PAGE VARCHAR
ITEMS VARCHAR
where ITEMS holds the list of items in a format that can be easily handled by your code, for example in JSON like ["ITEM1", "ITEM2", "ITEM3", ...]
This let you make a reordering with a single update on the database but an extra effort on the software to generate the ITEMS string. You can also change it to a different format like ITEM1,ITEM2,ITEM3 or ITEM1|ITEM2|ITEM3.
This format helps you also finding pages containing a specific ITEM because you can do a simple SELECT ID_PAGE FROM PAGES WHERE ITEMS LIKE '%<ID_ITEM>%'
Items table contains an AUTO_INCREMENT number unique to each sortable item on the pages.
Pages table contains a VARCHAR with a commalist of the ids of items.
The UI fetches that commalist, plus other info about the items for the user to rearrange. After each rearrangement, the list of ids is sent back to the server (via AJAX). Also sent back is the page_id. Then is is a simple
UPDATE Pages
SET items = ?
WHERE page_id = ?
Note that items is both the list of items and the ordering of those items. When "building" a page, split() (explode(), or whatever) the ids apart for further fetching. Or simply feed into an IN(...) list.
This will fetch the items for one page, and deliver them in the current order:
SELECT *
FROM Items
WHERE item_id IN ($item_list)
ORDER BY FIND_IN_SET(item_id, ?)
(Caveat: An IN list cannot be bound via ? in most APIs, you must construct the SQL after verifying that the list has not been hacked. Checking with regexp /^[,0-9]+$/ should suffice.)
My Items forces you to have a single table for items, at least for having a way to have unique item_ids. You may also require 3 other tables -- depending on schema details.
Only Rearrange?
If the user is only allowed to rearrange the items on one page, ...
Let's say, the page has items 51,4,7. This came from a SELECT that was used to build the UI.
If the user just rearranges them to, say, 7,51,4, take each string, explode it, sort numerically, then implode it. Compare them. Since both say 4,7,51, it is valid to UPDATE the items column. (Note: The "old" items should be re-fetched from the table just to make sure nothing hacked in the API call.)
A hacker may try for 99,33. But that will sort to 33,99, which won't match 4,7,51. (Ditto for a legitimate user trying to move items out of or into the page.)

MySQL on duplicate key delete

I am looking for a (not too convoluted) solution for a MySQL problem. Say I have the following table (with a joint index on group and item):
Group item
nogroup item_a
group_a item_a
Then, eventually, item_a no longer belongs to group_a. So I want to do something like:
update table set group = "nogroup" where item = "item_a" on duplicate key delete.
(obviously this is not a valid symtax but I am looking for a way around this)
I still want to keep a copy of the record with nogroup because, if later on, item_a comes back, i can change its group back to group_a or any other group depending on the case. Whenever item_a is added, there is an insert and it copies all the data from the nogroup record and sets a proper group label. At that point there are two records for item_a: one with group_a and one with no group. The reason it is done this way is to reuse previous data as much as possible as a new entry(with no previous record) is much more involved and take significantly more time and processing.
Say an item belongs to group_a and group_b but suddenly it does not belong to any group: the first update to set group to "nogroup" will work but the second update will create a duplicate key entry error.
The option of "not updating the group column at all" and using "insert on duplicate key update" does not work because there won't be duplicates when the groups are different and this will lead to cases where an item does not belong to a group anymore and yet the record will still be present in the database. The option of verifying if "nogroup" exists first and then updating it to a specific group does not work either because if item_a belongs to more than one group this would update all other records to the same group.
Basically, an item can belong to 1) any number of groups including "nogroup" or 2) solely belonging to "nogroup" and there should always be a copy of at least nogroup somewhere in the database.
It looks like I won't be able to do this in just one query but if someone has a clean way of dealing with this, that would be much appreciated. Maybe some of my assumptions above are wrong and there is an easy way to do it.
Your whole process of maintaining this items-to-groups mapping sounds too complicated. Why not just have a table that has a mapping? Then, when an item is removed from a group, delete it from the table. When it is added, add it to the table. Don't bother with "nogroup".
If you want an archive table, then create one. Have an insert/update/delete trigger (whichever is or are appropriate) that will populate an archive with information that you want to keep over time.
I do not understand why re-using an existing row would be beneficial in terms of performance. There is no obvious database reason why this would be the case.
I am also confused as to why you need a "nogroup" tag at all. If you need a list of items, maintain that list in its own table. And call the table Items -- a much clearer name than "nogroup".
I agree with Gordan's approach. However if you have to do it with a single table it cannot be done in 1 SQL query. You will have to use 2 queries 1 for update and 1 for delete.

Need help on Mysql Database Structure

I have 200 users each user will eventually have a "reviewINFO" table with certain data.
Each user will have a review every 3 to 4 months
So for every review, it creates a new row inside the "reviewINFO" table.
This is where i'm stuck. I'm not sure if I need to serialize a table inside each row or not.
Example:
-> links
"USER1reviewINFO"-row1->USER1table1
-row2->USER1table2
-row3->USER1table3
-row4->USER1table4
-row5->USER1table5
"USER2reviewINFO"-row1->USER2table1
-row2->USER2table2
-row3->USER2table3
-row4->USER2table4
-row5->USER2table5
using this method it will make a couple of thousand rows within two years. And I think its harder to manage.
"Userxtablex" is a table with dynamic rows of children names,ages,boolean
What i'm think of doing is serialize each USERxtable into its corresponding row.
Please help as I would not like to make this complicate or inefficient
Generally, you should never have to serialize data of this nature into a table row to accomplish what your goal is (which I am assuming is an implicit link between a user and a review)
What you need to do is key the reviews by a user_id such that all the reviews are packaged in one table, and relate numerically back to the users table.
Assuming you have an AUTO_INCREMENT primary key in the user table, all you would need is a user_id field in the reviews table that represents what user the review relates to. There is no need for a separate structure for each user, if that's what you are suggesting. Reviews can have date fields as well, so you can perform queries for a specific year or window of time.
You can then use a JOIN query to select out your data set relating to a particular user or review, and apply the usual WHERE clause to determine what result set you want to fetch.

Table join--multiple rows to/ from one column (/ cell)

I have searched for a solution for this problem, but haven't found it (yet), probably because I don't quite know how to explain it properly myself. If it is posted somewhere already, please let me know.
What I have is three databases that are related to each other; main, pieces & groups. Basically, the main database contains the most elementary/ most used information from a post and the pieces database contains data that is associated with that post. The groups database contains all of the (long) names of the groups a post in the main database can be 'posted in'. A post can be posted in multiple groups simultaneously. When a new post is added to my site, I check the pieces too see if there are any duplicates (check if the post has been posted already). In order to make the search for duplicates more effective, I only check the pieces that are posted in the same group(s).
Hopefully you're still with me, cause here's where it starts to get really confusing I think (let me know if I need to specify things more clearly): right now, both the main and the pieces database contain the full name of the group(s) (basically I'm not using the groups database at all). What I want to do is replace the names of those groups with their associated IDs from the groups database. For example, I want to change this:
from:
MAIN_table:
id  |  group_posted_in
--------|---------------------------
1   | group_1, group_5
2   | group_15, group_75
3   | group_1, group_215
GROUPS_table:
id  |  group_name
--------|---------------------------
1   | group_1
2   | group_2
3   | group_3
etc...
into:
MAIN_table:
id  |  group_posted_in
--------|---------------------------
1   | 1,5
2   | 15,75
3   | 1,215
Or something similar to this. However, This format specifically causes issues as the following query will return all of the rows (from the example), instead of just the one I need:
SELECT * FROM main_table WHERE group = '5'
I either have to change the query to something like this:
...WHERE group = '5' OR group = '5,%' OR group = '%,5,%' OR group = '%,5'
Or I have to change the database structure from Comma Separated Values to something like this: [15][75]. The accompanying query would be simpler, but it somehow seems like a cumbersome solution to me. Additionally, (simple) joins will not be easy/ possible at all. It will always require me to run a separate query to fetch the names of the groups--whether a user searches for posts in a specific group (in which case, I first have to run a query to fetch the id's, then to search for the associated posts), or whether it is to display them (first the posts, then another query to match the groups).
So, in conclusion: I suppose I know there is a solution to this problem, but my gut tells me that it is not the right/ best way to do it. So, I suppose the question that ties this post together is:
What is the correct method to connect the group database to the others?
For a many-to-many relationship, you need to create a joining table. Rather than storing a list of groups in a single column, you should split that column out into multiple rows in a separate table. This will allow you to perform set based functions on them and will significantly speed up the database, as well as making it more robust and error proof.
Main
MainID ...
Group
GroupID GroupName
GroupsInMain
GroupsInMainID MainID(FK) GroupID(FK)
So, for MainID 1, you would have GroupsInMain records:
1,1,1
2,1,5
This associates groups 1 and 5 with MainID 1
FK in this case means a Foreign Key (i.e. a reference to a primary key in another table). You'd probably also want to add a unique constraint to GroupsInMain on MainID and GroupID, since you'd never want the same values for the pairing to show up more than once.
Your query would then be:
select GroupsInMain.MainID, Group.GroupName
from Group, GroupsInMain
where Group.GroupID=GroupsInMain.GroupID
and Group.GroupID=5

Mysql Insert data into table have a arranges question?

I found a weard problem with my MySQL DB.
sometime when I insert new data into it, the way it arranges the data is like a stack, for example
4 (newest)
3
2
1 (oldest)
...
how can I make it arranged like this?
1 (newest)
2
3
4 (oldest)
thanks all.
SELECT *
FROM TABLE
ORDER BY ID
You have to remember that when viewing/selecting data from a table without any ORDER BY specified does not garuantee any specific order.
The way you are view the data (unordered) can be due to any one of a lot of factos (the database engine, schema, page storage, page fragmentation, indexes, primary keys, or simply execution plan optimization).
The SQL standards specifically states that tables do not have a "natural" order. Therefore, the database engine is free to return a request without an ORDER BY in any order it wants to. The order may change from one request to another because most engines choose to return the data in whatever order they can get it to you the most rapidly.
It follows, therefore, that if you want the data out in a particular order you must include a column in your table whose job is to proxy for the order in which you added records to the table. Two common ways of doing this are using an autoincrement field which will be in numerical order from oldest to newest record, and a TIMESTAMP column which does just what it says. Once you have such a column you can use ORDER BY ColumnName when searching to get an ordered result set.