views:

155

answers:

3

basically i have albums, which has 50 images init.. now if i show list of images, i know from which to which row is showing (showing: 20 to 30 of 50), means showing 10 rows from 20 - 30. well now the problem is, i want to select an image, but still show which postion was it selected, so i can move back and forth, but keep the postion too.

like if i select 5th image, which id is 'sd564', i want to show (6 of 50 images), means you are seeing 6th of 50 images.. if i get next row id and show that, then, i want to show (7 of 50 images).

well i can do all this from pagination pointer easily, like in url say (after=5, after=6)... its moving with postion, but what if i dont have this (after=6) and just have an id, how can i still do that?

i dont want to use (after=6) also because its dynamic site and images adds and delete, so position chnages and sharing with someone else and going back on same old link, then it would be wrong position.

what kind of sql query should i be running for this?

currently i have

select * from images where id = 'sd564'; 

obviously i need to add limit or some other thing in query to get what i want or maybe run another query to get the result, while keeping this old query inplace too. anyway i just want positioning. i hope you can help me solve this

Example: http://media.photobucket.com/image/color%20splash/aly3265/converse.jpg

sample

Album Query Request (check post below)

select images.* from images, album
where album_id = '5'
and album_id = image_album_id
order by created_date DESC
limit ....;
A: 

From what you are saying here:

dont want to use (after=6) also because its dynamic site and images adds and delete, so position chnages and sharing with someone else and going back on same old link, then it would be wrong position.

I get the impression that this is not a SQL problem at all. The problem is that the positions of the fotos are local to the search resultset. To reliably naviate by position, you would need to make a snapshot (no pun intended) of some kind. That is, you need to have some way to "freeze" the dataset while it is being browsed.

A simple way to do it, would be to execute the search, and cache the result outside of the actual current datastore. For example, you could use "scratch tables" in your database, simply store it in temporary files, or in some memory caching layer if you have the mem for it. With this model, you'd let the user browse the resultset from the cache, and you would need to clean out the cache when the user's session ends (or after some timeout, you don't want to kill your server because some users don't log out)

Another way to do it, is to simply allow yourself to lie now and then. Let's say you have result pages of 10 images, and a typical search delivers 50 pages of results. Well, you could simply send a resultset for a fixed number of items, say 100 photos (so 10 pages) to the client. These search results would then be your snapshot, and contain references to the actual pictures. If you are storing the URLS in the database , and not the binary data, this reference is simply the URL. Or you could store the database Id there. Anyway, the user is allowed to browse the initial resultset, and chances are that they never browse the entire set. If they do, you re-execute the query on the server side for the next chunk of pages. If many photos were added in the mean time that would end up at positions 1..100, then the user will see stale data: that's the price they pay for having so much time on their hands that they can allow themselvs to browse 10 pages of 10 photos.

(of course, you should tweak the parameters to your liking but you get the idea I'm sure.)

If you don't want to 'lie' and it is really important that people can reliably browse all the results they searched, you could extend your database schema to support snapshots at that level. Now asssuming that there are only two operations for photos, namely "add" and "delete", you would have a TIMESTAMP_ADDED and a TIMESTAMP_REMOVED in your photo table. On add, you do the INSERT in your db, and fill TIMESTAMP_ADDED with the currrent timestamp. The TIMESTAMP_REMOVED would be filled with the theoretical maximum value for whatever data type you like to use to store the timestamp (For this particular case I would probably go for an INT column and simply store the UNIX_TIMESTAMP) On delete, you don't DELETE the row from the db, rather, you mark it as deleted by updating TIMESTAMP_REMOVED column, setting it to the current timestamp. Now when you have to do a search, you use a query like:

SELECT    *
FROM      photo
WHERE     timestamp_added   < timestamp_of_initial_search
AND       timestamp_removed > timestamp_of_initial_search
AND      ...various search criteria...
ORDER BY ...something
LIMIT    ...page offset and num items in page...

The timestamp_of_initial_search is the timestamp of executing the initial search for a particular set of criteria. You should store that in the application session while the user is browsing a particular search resultet so you can use that in the subsequent queries required for fetching the pages. The first two WHERE criteria are there to implement the snapshot. The condition timestamp_added < timestamp_of_initial_search ensures we can only see photos that were added before the timestamp of executing the search. The condition timestamp_removed > timestamp_of_initial_search ensures we only search that were not already removed by the time the initial search was executed.

Of course, you still have to do something with the photos that were marked for delete. You could schedule periodical physical deletion for all photos that have a timestamp removed that is smaller than any of the current search resultsets.

Roland Bouman
well i want to thank you for posting, but im sure there would be a way to see the position without needing to do snapshot.. if any value changes in the row position, because of updates/insert/delete. thats fine. but making snapshot, thats just too much i think, and it is possible without it.
Basit
The problem is that the position is a property that is derived from being in the search request's resultset. Either the search result is deterministic, in which case you can always recreate the position when you execute the search another time, or, as you described, the underlying store can change in the mean while, making the search result non-deterministic. In that case, you either have to live with inconsistent results when navigating by position, or temporarily store the positions for each particular search result. The last solution makes the result deterministic through a snapshot.
Roland Bouman
well the thing im doing for, its for albums.. so only if user add images to the album, then position will change or if he delete it, else it will be pretty much same. so lets say i wanted to do it with search result, how can i do it? obviously i only know image id, so what query would i have to run, to find out which poition is the current row at, without pulling all the rows or more then one row. cause query would get very slow, if we head to pull all the rows and stuff, which is very un-professional too i think.
Basit
From your picture, I got the impression you are working with search results, not albums. For albums, I would store a position-per-album for each photo (along with the album id). Then it becomes trivial to navigate by position. If you don't explicitly store the position, the position in the search result is dependent upon the ORDER BY clause of the search query (If you don't specify that, there is no inherent order). You can use either a `LIMIT offset,count` clause to pinpoint a specific position, else you need either a self-join or a subquery to compute the row number, and then filter on that
Roland Bouman
If you post the query you use to list all photos from one album, I can show how you could write the query.
Roland Bouman
i added small sql query above, to show how im getting album listing. but its not complete query, cause complete query have permission table and user table, which dose not really need to be written here, it will confuse everyone. but if you can show how to show image id 'sd564' while getting the postion too, i think i can do the rest part. :) thank you
Basit
@basit: so you don't impose any order on your fotos?! At least there is no ORDER BY in your query. That means that today i can view the album and get the pictures in this order: s123, s548, s900 and an hour later, I can view the album again and get s548, s123, s900; Tomorrow, I view the album again and get s900, s548, s123...See, the results are returned in some order, but whatever order that is, it depends on the database. You can't rely on the position of rows in a resultset unless you explicitly order them using ORDER BY. So in the current form, it is not possible to do what you want.
Roland Bouman
well it does have order by, but i just wrote query to see how would you make a query to get the position, then ill adjust it as i need it. but on above example, i just added order by too, like its on album.
Basit
Ok, so now you do have an order by, but it is not absolute: it is possible that you end up with two or more photos in the same album having the same created_date. Do you photos have an auto_increment id column? If so, then we can use that.
Roland Bouman
no we dont have auto_increment id colum. and no created date will never be the same, unless images was uploaded by machine or something. :)
Basit
A: 

If I understood your problem correctly, you can use the Row_Number() function (in SQL Server). To get the desired result, you can use a query something similar to this:

select images1.* from 
    (SELECT ROW_NUMBER() OVER (ORDER BY image_album_id) as rowID,(SELECT COUNT(*) FROM images) AS totCount, * FROM images) images1
JOIN album ON (album_id = images1.image_album_id)
where album_id = '5' 
order by images1.image_album_id 
limit ....;

Here the images.rowid gives you the position of the row and images.totCount give you the total number of rows.

Hope it helps.

Thnks.

rookie
@rookie, this 'solution' does not guarantee that a second query that restricts a row by position returns the same row every time. You are ordering on image_album_id, which is not unique for the image table. What may be row 1 now, may be row 10 some other time. In addition, MySQL does not support ROW_NUMBER() (the question is tagged mysql so I assume it must work for that.)
Roland Bouman
In the order by clause, we can use a unique field for the image table, which solves the problem. In my answer, i said the query should be something like this. So who ever using the query can fine tune it based on their table structure.Also what you said is correct, ROW_NUMBER() will work with SQL server, but there should be some equivalent in MySQL, am not familiar with MySQL syntax.May be http://stackoverflow.com/questions/1895110/rownumber-in-mysql will help
rookie
rookie, yes you can. but look at the discussion beneath my initial answer. It took some effort to get basit to explain about how he feels the order should be constructed, so IMO that was not a trivial ingredient, at least, apparently not to him.
Roland Bouman
the only thing i think its missing from this too, is the image id single selection and mysql query example. if you look at my very first post and example, you will see i used image_id and not album_id, ofcourse i can get album_id too, but image_id is must use to pull that image detail only and not all the listings..
Basit
+2  A: 

Assuming created_date is unique per album_id and (album_id,created_date) is unique for all rows in images, then this:

select     i1.*, count(*) as position
from       images i1
inner join images i2
on         i1.album_id      = i2.album_id     -- get all other pics in this album
and        i1.created_date >= i2.created_date -- in case they were created before this pic
where      i1.album_id = 5
group by   i1.created_date

will reliably get you the images and their position. Please understand that this will only work reliably in case (album_id,created_date) are unique throughout the images table. If that is not the case, the position wont be reliable, and you might not see all photos due to the GROUP BY. Also note that a GROUP BY clause like this, only listing some of the columns that appear in the SELECT list (in this case images.*) is not valid in most RDBMS-es. For a detailed discussion on that matter, see: http://dev.mysql.com/tech-resources/articles/debunking-group-by-myths.html

By doing this:

select     i1.*, count(*) as position
from       images i1
inner join images i2
on         i1.album_id      = i2.album_id     -- get all other pics in this album
and        i1.created_date >= i2.created_date -- in case they were created before this pic
where      i1.album_id = 5
group by   i1.created_date
having     count(*) = 4

you select the image at the 4th position (note the having count(*) = 4)

By doing this:

select     i1.*, count(*) as position
from       images i1
inner join images i2
on         i1.album_id      = i2.album_id     -- get all other pics in this album
and        i1.created_date >= i2.created_date -- in case they were created before this pic
where      i1.album_id = 5
group by   i1.created_date
having     count(*) between 1 and 10

you select all photos with positions 1 through 10 (note the having clause again.)

Of course, if you just want one particular image, you can simply do:

select     i1.*, count(*) as position
from       images i1
inner join images i2
on         i1.album_id      = i2.album_id     -- get all other pics in this album
and        i1.created_date >= i2.created_date -- in case they were created before this pic
where      i1.image_id = 's1234' 
group by   i1.created_date

This will correctly report the position of the image within the album (of course, assuming that image_id is unique with in the images table). You don't need the having clause in that case since you already pinpointed the image you want.

Roland Bouman
looks very neat, but what if i wanted to select one image, which is image_id = 'sd564'? would that get me the position of that too? and how would i put image_id, if i wanted to select that image only, cause thats the main purpose of the post.
Basit
basit, have you tried? if you did you will find that it works just like it should. for your convience I added an example query below the other examples.
Roland Bouman
i tried it now, it works, but the only problem left is the postion start from up(99) to down(1). it should start the postion from down(1) to up(99). the first row postion is 99 and last row postion is 1..
Basit
changing the created_date >= to <=.. makes it start working fine :)
Basit
Roland im very pleased with your solution. i just have one question.. is there a way to put mili-seconds too on the date, to make it uniqe for each upload.. incase if it ever head problem.
Basit
@basit: that is what I meant when I said: "if i'd be doing albums, I'd be storing a position". The thing is, in MySQL the timestamps and datetimes do not have subsecond resolution. You can of course make an integer column and store a timestamp + miliseconds generated from your language (php?) in there. But even simpler would be to simply use an auto_increment column for the photos. the db gives out the numbers in order, so if you select per album, and sort by photo_id, you always get a almost 100% perfect order.
Roland Bouman
maybe i can create order by colume and let it increment.. sence changing the id is not an option for me.. what you think?
Basit
auto increment works only for primary key, so maybe i can create two id's.. one encrypted one and one integer one, sence i cant remove the old id.. put index on encrypted one and new one put auto increment. what you think? would that work, is it wrong or right to do that?
Basit
also you said about doing timestamp + miliseconds with php.. if i do that, do you think then all rows will be uniqe? and not same?
Basit
basit, what is your table structure? Personally, I would have album (id, user_id, name, description, ...other columns) with id primary key and auto_increment, and user_id and name unique. album_photo(id, album_id, date_created, description) with id primary key and auto_increment and album_id a foreign key to album.About the miliseconds from php: no, you never have the guarantee that it will be unique. it's the nature of time. you will just get less conflicts if the time units are smaller.
Roland Bouman
well my structure is big and invovle some permission tables and much more.. but the only difference it would have from your structure, it would be the auto-increment, im not doing that, cause id need to be uniqe and more like youtube id's.. but ill have another id behind it, just to have this position thing.. sence auto-increment is only allowed on primary key. so i would have two id's, but one would be acutally used and other one would be hidden and would only be used for auto-increment.. sence we going with id.. so i would only have to change create_date with id field in query right?
Basit
basit, I don't understand what you are saying about the having two ids. Please answer this simple question: what is the primary key of your photo-in-album table?
Roland Bouman
its same as its for media id.. which is not auto-increment, its 11 letters id, like youtube id's
Basit
So why don't you just create a primary key that's auto_increment, and keep your 'real' primary key as a unique not null column? codd won't mind...and it will make your like a lot simpler. functionally there is no difference between a primary key and a unique constraint provided all columns are not nullable.
Roland Bouman
ok. thank you soo much for helping me.. really appreciate it. some day maybe i can return the favor. :)
Basit
Roland the query been working fine, but sometimes it gives problem. sometimes there are only 8 rows, but it shows the position 183 or 96 or 63, depending on which user album is getting previwed, but usally works fine. do you know what it can be? following is the complete query im using http://pastie.org/817093
Basit
Can you please paste the exact table definitions, including primary / unique keys? ty.
Roland Bouman
http://pastie.org/817353
Basit
did you got the link.. this was the link to what you requested http://pastie.org/817353
Basit
Hi @basit. I think you left out a rather important condition in you JOIN. in `from media media inner join media m2 on media.album_id = m2.album_id and media.id <= m2.id ` you should probably add a condition for `media.user_id = m2.user_id`
Roland Bouman
lol.. i will try that.. and reply back.. if it works.. i will be in heaven, if not, then im working in hell lol hehe.. but really, thank you :)
Basit
man im sooo happy that work, it dont make sence why i have to put that condition too, but woo hoo.. hmm i guess maybe cause album_id matchs with other user album id..hmmm.. thank you tho, appreciate it very much. i could'nt have done without you. thanks.
Basit
@Roland Bouman can you help on this please http://stackoverflow.com/questions/3366597/how-to-make-slow-query-faster
Basit