I have a column called list which is used in my order by (in MYSQL queries) and within list is numbers: (e.g. 1 to 20)
This list is then output using MYSQL order by list ASC - However, when I update my list in backend using a Jquery drag drop UI list it is supposed to update the list frontend.
My problem is that my list order sometimes conflicts with other rows as there could be two or three rows with the value of 1 in list therefore when my order updates I would like to know how I can update other rows by +1 only if the rows are >= the order number given
I do not want to make the column primary as I am not aiming to make the list column unique, the reason for this is because there is more than one category - and in each category they all start at 1 - therefore if I make it unique it would cause errors because there was multiple 1's over different categories.
I asked a friend who said I could probably try PL/SQL using a trigger function but this is new grounds to me - I don't fully understand that language and was wondering if anyone could help me do what I am trying to using MYSQL or even PL/SQL.
This is what I have so far:
<?php
$project = mysql_real_escape_string(stripslashes($_POST['pid']));
$category = mysql_real_escape_string(stripslashes($_POST['cat']));
$order = mysql_real_escape_string(stripslashes($_POST['order']));
// need to do update the moved result (list row) and update all conflicting rows by 1
mysql_query("UPDATE `projects` SET `cat`='$category',`list`='$order' WHERE `id`='$project'")or die(mysql_query());
?>
Conclusion:
I am trying to update a none unique column to have unique values for that individual category. I am not sure how to update all the rows in that category by +1
#andrewsi is right, in particular I suggest order by list ASC, last_update DESC so in the same query where you update list you can timestamp last_update and therefore you will have not need to use triggers or any other updates.
In general, what andrewsi and Luis have suggested is true. Instead of (like andrewsi said) "do messy updates" you should really consider ordering by a second column.
However, I can maybe see your point for your approach. One similar situation I know it could apply is in a CMS where you let the backend user order items by changing the order number manually in textfields next to the items, e.g.
item 1 - [ 1 ]
item 2 - [ 3 ]
item 3 - [ 2 ]
... the number in the [] would then be the new order.
So, a quite messy solution would be (many steps, but if you do not have to worry about performance it might be OK for you, I don't know):
INSERT INTO projects (cat, list, timestamp_inserted) VALUES (:cat, :list, NOW())
and then as a second step
SELECT id, list FROM projects WHERE cat=:cat ORDER BY list ASC, timestamp_inserted DESC
and then loop through the array you get from the select and foreach row update (:i is the increasing index)
UPDATE projects SET list=:i WHERE id=:id
PS: you would have to add a column timestamp_inserted with a timestamp value.
PPS: to clearly state, I would not recommend this and never said it is best practice (for those considering to downvote because of this)
Related
I'm having some trouble coming up with an efficient solution to this problem. Maybe I am making it more complicated than needs to be. I have a table like this:
thing_id | user_id | order
1 1 0
2 1 1
3 1 2
The user may mess around with their things and it may happen that they change thing 1 to thing 3, and thing 3 to thing 1. In my case, it is not that the user is explicitly changing the order. Rather, they are modifying their bank of things, and they may change the thing in slot 1 to be the thing in slot 3, and vice versa. So if the user performs this operation, the table should look like this:
thing_id | user_id | order
3 1 0
2 1 1
1 1 2
What complicates this is that (thing_id, user_id) has a unique constraint, so doing sequential updates does not quite work. If I try to UPDATE tbl SET thing_id=3 WHERE thing_id=1, the unique constraint is broken.
The order column is purely for show, in order to make an alphabetized list. So I suppose I could use PHP to check the order and figure things out like that, but this introduces code that really has nothing to do with the important stuff. I'd like to find a solution that is purely/mostly SQL.
Also, along the same lines, if I were to insert a new row into the table, I would want the order value to be 3. Is there an efficient way to do this in SQL, without first having to SELECT MAX(order) WHERE user_id=1?
My comment seems to have gotten some traction, so I'm posting it as an answer... To avoid your problem, add a new column, without constraints, and just use that for user desired updates.
Why aren't you updating the order instead of the thingid?
UPDATE tbl
SET order = 2
WHERE thing_id=1;
Each row represents a "thing-user" pair. The data is the ordering that you want to use. You don't want to change the entity ("thing-user"). You want to change the data.
By the way, you'll then have to do some additional work to keep unique values in orders.
If you switched this around and put the unique constraint on user_id, order, then it would make sense to update the thing_id.
I have one table called cf_posts
ID pk
user INT
subject VARCHAR
body TEXT
datetime TIMESTAMP
parent INT
category INT
mod INT
When a post is submitted to the forum the default parent is 0, when a post is submitted as a reply, then its parent is the ID of the original post.
How can I make it so that the default view of the forum main page is ordered that the most recently updated post (including the latest replies) would be at the "top" of the pile, and working down in date order? What would be the PHP/MySQL query?
The only workarounds I have seen for this are separate topics and reply tables, but I'd like to stay away from this approach if possible.
One workaround that I tried and failed was GROUP BY parent.
But this grouped all topics that had no replies together as one.
Another idea that I have yet to try is to make the parent id of the original post match the post ID, and not include matching ID and parent IDs in the output.
I look forward to hearing your thoughts.
SELECT mainPost.subject, lastPost.datetime
FROM cf_posts cfp,
(
SELECT *
FROM cf_posts subPost
WHERE subPost.parent = mainPost
ORDER BY subPost.datetime DESC
LIMIT 1
)lastPost
WHERE mainPost.parent IS NULL
This is done briefly, so there may be some syntax issues but I think this should help.
You can do the following: query each separate thing that you need, so maybe a query for each topic, Then you can use UNION to bunch all of them together to get one list. Now the trick to preserve an order is as followed. For each separate query append a column to the returned result called sort and set each instance of that to a higher int value, then you can guarantee that the final result is properly sorted. Go review UNION for select statements to get a better understanding of what I'm talking about.
I have a int(11) DisplayOrder column in my database of slideshow items which tells my code what order to display the items in. An admin can update the order with a form, changing, for example, number 20 to number 3. Without forcing the user to manually find number 3 and swap it with 20, is there an easy way to update the sequence of numbers in the database? Either using MySQL, or changing items in in array() before sending a query.
I've searched and searched, and there doesn't seem to be many solid answers. The issue is that I currently have 400 rows, and that number will grow. I don't really want to have to do 400 updates every time the order changes. Is there a better way? Are there existing scripts?
Why do you need to fully resequence the DisplayOrder values?
An ORDER BY clause will work just as well if there are duplicate values. To maintain a stable sort, you can order by a secondary column such as item name or id.
I have a table in MySQL that I'm accessing from PHP. For example, let's have a table named THINGS:
things.ID - int primary key
things.name - varchar
things.owner_ID - int for joining with another table
My select statement to get what I need might look like:
SELECT * FROM things WHERE owner_ID = 99;
Pretty straightforward. Now, I'd like users to be able to specify a completely arbitrary order for the items returned from this query. The list will be displayed, they can then click an "up" or "down" button next to a row and have it moved up or down the list, or possibly a drag-and-drop operation to move it to anywhere else. I'd like this order to be saved in the database (same or other table). The custom order would be unique for the set of rows for each owner_ID.
I've searched for ways to provide this ordering without luck. I've thought of a few ways to implement this, but help me fill in the final option:
Add an INT column and set it's value to whatever I need to get rows
returned in my order. This presents the problem of scanning
row-by-row to find the insertion point, and possibly needing to
update the preceding/following rows sort column.
Having a "next" and "previous" column, implementing a linked list.
Once I find my place, I'll just have to update max 2 rows to insert
the row. But this requires scanning for the location from row #1.
Some SQL/relational DB trick I'm unaware of...
I'm looking for an answer to #3 because it may be out there, who knows. Plus, I'd like to offload as much as I can on the database.
From what I've read you need a new table containing the ordering of each user, say it's called *user_orderings*.
This table should contain the user ID, the position of the thing and the ID of the thing. The (user_id, thing_id) should be the PK. This way you need to update this table every time but you can get the things for a user in the order he/she wants using ORDER BY on the user_orderings table and joining it with the things table. It should work.
The simplest expression of an ordered list is: 3,1,2,4. We can store this as a string in the parent table; so if our table is photos with the foreign key profile_id, we'd place our photo order in profiles.photo_order. We can then consider this field in our order by clause by utilizing the find_in_set() function. This requires either two queries or a join. I use two queries but the join is more interesting, so here it is:
select photos.photo_id, photos.caption
from photos
join profiles on profiles.profile_id = photos.profile_id
where photos.profile_id = 1
order by find_in_set(photos.photo_id, profiles.photo_order);
Note that you would probably not want to use find_in_set() in a where clause due to performance implications, but in an order by clause, there are few enough results to make this fast.
I am faced with a problem coding my next feature. I want the user to be able to rearrange records and change the display_order value. I'm using Jquery UI's draggable and droppable to facilitate this.
I can see how a simple swap of display_order values would work. But I want to set a display order for a record and ideally have the others shuffle around so there are no repeated display_order values. Apart from not getting my head around how I would do that it seems like it would be hard to code and inefficient, shuffling every value around in the list.
So I am open to other suggestions of how this sort of thing is normally, or should be done.
I though of maybe using a value like 3.000 to represent the order and then when I want to make a record take its place make its value 3 - 0.001 so its 2.999 and will sort between 2 and 3. But I can see so many things wrong with that idea and doesn't seem like a good path to follow.
Thanks for any input!
add a sorting column to your table, smallint,
mediumint or int depending on the
expected number of total entries
A new entry is appended to the end, max(sorting) + 1
when reordering an item, get the new position it will be in and alter the higher sorting entries accordingly:
mysql_query('UPDATE yourTable set sorting = '.$yourNewposition.' where id='.$yourUniqueId .' LIMIT 1');
mysql_query('UPDATE yourTable set sorting = sorting + 1 where sorting >= '.$yourNewposition.' AND id != '.$yourUniqueId );