I have hundreds rows of data. I want to get the previous row and the next row from the current item. I saw many examples here but none solve my problem.
My data key is a set of string (md5) which is unable to compare the greater or lower like the other does. Here's my resources.
---------------------------------------------------
| id| sid | name |
----+----------------------------------+----------|
| 1 | c81e728d9d4c2f636f067f89cc14862c + Mr.A |
----+----------------------------------+----------|
| 2 | eccbc87e4b5ce2fe28308fd9f2a7baf3 | Mr.B |
----+----------------------------------+----------|
| 3 | a87ff679a2f3e71d9181a67b7542122c | Mr.C |<current position
----+----------------------------------+----------|
| 4 | e4da3b7fbbce2345d7772b0674a318d5 | Mr.D |
----+----------------------------------+----------|
| 5 | 1679091c5a880faf6fb5e6087eb1b2dc | Mr.E |
--------------------------------------------------|
So, is there any way to get the previous row (Mr.B) and the next row (Mr.D) with mysql?
I've tried
SELECT * FROM table
WHERE sid < #sid
ORDER BY sid DESC
LIMIT 1
but it's not work because sid is uncomparable.
Related
I have 6 tables to store my user data
tbl_user_name
uuid | username | date (unix time)
-----------------------------------------
0 | lisha.s | 1489857236
1 | titami | 1485853232
2 | mathew | 1442853636 <----|> Users can change their username
3 | sandarjun | 1489857239 |> so i need to get the uuid by the
2 | mathew_kar | 1575456274 <----|> newest username which is given
tbl_user_fullname:
uuid | fullname | date (unix time)
-----------------------------------------
0 | Lisha Simonette | 1489857236
1 | Titus Amiran | 1481256345
2 | Mathew Karolina | 1489234455
3 | Sandhya Arjun | 1489857239
0 | Lisha Karolina | 1599999999
tbl_user_website:
uuid | website | date (unix time)
-----------------------------------------
0 | google.com | 1489857236
1 | titusamiran.com | 1489855234
2 | mathewk.net | 1489857432
3 | blod.sandhya.info | 1489857239
tbl_user_birthdate:
uuid | birthdate | date (unix time)
-----------------------------------------
0 | 02-05-1991 | 1489857236
1 | 05-08-1980 | 1489857123
2 | 09-09-1992 | 1489851334
3 | 17-02-1998 | 1489857239
tbl_user_follower:
uuid | follower_uuid
-----------------------
0 | 4
1 | 8
2 | 0
3 | 4
3 | 2
3 | 1
tbl_user_online:
uuid | last_seen (unix time)
-----------------------
0 | 1489855334
1 | 1589851111
2 | 1689857234
3 | 1789834539
i want to collect the uuid, the fullname, the website, the birthdate and the number of followers by a user with a given username. (i need to get the newest uuid of a username because the user can change the username).
The date column is the timestamp when they changes a value. for example in tbl_user_fullname: Lisha Simonette (uuid 0) married Mathew Karolina (uuid 2) so i neet to get the new fullname of Lisha (uuid 0) by the date column. And so on ... for tbl_user_website and tbl_user_birthdate .. even if they dont change their birthday often ;)
From the table tbl_user_online i only need the last_seen timestamp.
The value i give to the query is the username. the username should give out the uuid with which i can query the other tables.
Thank you very much for your help and sorry for my bad english ;)
The below query will resolve the question asked:
SELECT usr.uuid,
ful.fullname,
web.website,
brt.birthdate,
COUNT(fol.follower_uuid)
FROM tbl_user_name usr
JOIN tbl_full_name ful ON ful.uuid = usr.uuid
LEFT JOIN
tbl_full_name ful2 ON ful2.uuid = ful.uuid
AND ful2.time_stamp > ful.time_stamp
JOIN tbl_user_website web ON web.uuid = usr.uuid
LEFT JOIN
tbl_user_website web2 ON web2.uuid = web.uuid
AND web2.time_stamp > web.time_stamp
JOIN tbl_user_birthdate brt ON brt.uuid = usr.uuid
LEFT JOIN
tbl_user_birthdate brt2 ON brt2.uuid = brt.uuid
AND brt2.time_stamp > brt.time_stamp
JOIN tbl_user_follower fol ON fol.uuid = usr.uuid
WHERE usr.username = 'jack_2'
AND ful2.uuid IS NULL
AND web2.uuid IS NULL
AND brt2.uuid IS NULL
GROUP BY
usr.uuid,
ful.fullname,
web.website,
brt.birthdate
This query works by isolating the latest timestamp in a table that shares a common element in a row. In this case the uuid.
Here is the fiddle to see it working.
Here is a fiddle without the username filter
You have a bigger issue that you need to resolve, however. You should not be storing values like you are with timestamps.
I suggest you look into table triggers for update and insert statements. You should have a trigger set up to automatically insert an entry into an audit table that stores this information while keeping your core functioning tables small and orderly.
e.g create a table called tbl_user_name_audit. Have a trigger on tbl_user_name for updates. When a username is updated the previous value is inserted into the tbl_user_name_audit table with a before and after value as well as a timestamp and audit type (INSERT/UPDATE/DELETE).
I have a table of food items. They have a "Position" field that represents the order they should appear in on a list (listID is the list they are on, we don't want to re-order items on another list).
+--id--+--listID--+---name---+--position--+
| 1 | 1 | cheese | 0 |
| 2 | 1 | chips | 1 |
| 3 | 1 | bacon | 2 |
| 4 | 1 | apples | 3 |
| 5 | 1 | pears | 4 |
| 6 | 1 | pie | 5 |
| 7 | 2 | carrots | 0 |
| 8,9+ | 3,4+ | ... | ... |
+------+----------+----------+------------+
I want to be able to say "Move Pears to before Chips" which involves setting the position of Pears to position 1, and then incrementing all the positions inbetween by 1. so that my resulting Table look like this...
+--id--+--listID--+---name---+--position--+
| 1 | 1 | cheese | 0 |
| 2 | 1 | chips | 2 |
| 3 | 1 | bacon | 3 |
| 4 | 1 | apples | 4 |
| 5 | 1 | pears | 1 |
| 6 | 1 | pie | 5 |
| 7 | 2 | carrots | 0 |
| 8,9+ | 3,4+ | ... | ... |
+------+----------+----------+------------+
So that all I need to do is SELECT name FROM mytable WHERE listID = 1 ORDER BY position and I'll get all my food in the right order.
Is it possible to do this with a single query? Keep in mind that a record might be moving up or down in the list, and that the table contains records for multiple lists, so we need to isolate the listID.
My knowledge of SQL is pretty limited so right now the only way I know of to do this is to SELECT id, position FROM mytable WHERE listID = 1 AND position BETWEEN 1 AND 5 then I can use Javascript (node.js) to change position 5 to 1, and increment all others +1. Then UPDATE all the records I just changed.
It's just that anytime I try to read up on SQL stuff everyone keeps saying to avoid multiple queries and avoid doing syncronous coding and stuff like that.
Thanks
This calls for a complex query that updates many records. But a small change to your data can change things so that it can be achieved with a simple query that modifies just one record.
UPDATE my_table set position = position*10;
In the old days, the BASIC programming language on many systems had line numbers, it encouraged spagetti code. Instead of functions many people wrote GOTO line_number. Real trouble arose if you numbered the lines sequentially and had to add or delete a few lines. How did people get around it? By increment lines by 10! That's what we are doing here.
So you want pears to be the second item?
UPDATE my_table set position = 15 WHERE listId=1 AND name = 'Pears'
Worried that eventually gaps between the items will disappear after multiple reordering? No fear just do
UPDATE my_table set position = position*10;
From time to time.
I do not think this can be conveniently done in less than two queries, which is OK, there should be as few queries as possible, but not at any cost. The two queries would be like (based on what you write yourself)
UPDATE mytable SET position = 1 WHERE listID = 1 AND name = 'pears';
UPDATE mytable SET position = position + 1 WHERE listID = 1 AND position BETWEEN 2 AND 4;
I've mostly figured out my problem. So I've decided to put an answer here incase anyone finds it helpful.
I can make use of a CASE statement in SQL. Also by using Javascript beforehand to build my SQL query I can change multiple records.
This builds my SQL query:
var sql;
var incrementDirection = (startPos > endPos)? 1 : -1;
sql = "UPDATE mytable SET position = CASE WHEN position = "+startPos+" THEN "+endPos;
for(var i=endPos; i!=startPos; i+=incrementDirection){
sql += " WHEN position = "+i+" THEN "+(i+incrementDirection);
}
sql += " ELSE position END WHERE listID = "+listID;
If I want to move Pears to before Chips. I can set:
startPos = 4;
endPos = 1;
listID = 1;
My code will produce an SQL statement that looks like:
UPDATE mytable
SET position = CASE
WHEN position = 4 THEN 1
WHEN position = 1 THEN 2
WHEN position = 2 THEN 3
WHEN position = 3 THEN 4
ELSE position
END
WHERE listID = 1
I run that code and my final table will look like:
+--id--+--listID--+---name---+--position--+
| 1 | 1 | cheese | 0 |
| 2 | 1 | chips | 2 |
| 3 | 1 | bacon | 3 |
| 4 | 1 | apples | 4 |
| 5 | 1 | pears | 1 |
| 6 | 1 | pie | 5 |
| 7 | 2 | carrots | 0 |
| 8,9+ | 3,4+ | ... | ... |
+------+----------+----------+------------+
After that, all I have to do is run SELECT name FROM mytable WHERE listID = 1 ORDER BY position and the output will be as follows::
cheese
pears
chips
bacon
apples
pie
I'm new posting here but the community have been my best resource on my projects so far.
I'm a dumb/dummy Mysql "wanna be" and I'm in the middle of a project that is making me go mad.
I have a table from wordpress plugin buddypress that pairs meta_key and meta_values in order to create something akin to a taxonomy. My duty is to use these paired values to implement an advanced group search. Here is the original table:
--------------------------------------------
id | group_id | meta_key | meta_value
--------------------------------------------
1 | 1 | time-zone | Kwajalein
2 | 1 | playstyle | hardcore
3 | 1 | recruiting-status | Open
4 | 1 | ilvl | 115
5 | 1 | main-raid | Final Coil of Bahamut
6 | 1 | voicechat | fc.teamspeak3.com
etc....
Using a view I managed to create a more friendly searchable table for begginers :
gid| time-zone| playstyle | main-raid
--------------------------------------------
1 | | |
1 |Kwajalein | |
1 | | hardcore |
1 | | |
1 | | | Final Coil of Bahamut
1 | | |
And here is the view code:
SELECT distinct
group_id AS 'gid',
IF(meta_key='recruiting-status',meta_value,'') AS 'Recruitment',
IF(meta_key='server',meta_value,'') AS 'server',
IF(meta_key='time-zone',meta_value,'') AS 'tzone',
IF(meta_key='main-raid',meta_value,'') AS 'raid',
IF(meta_key='raid-days',meta_value,'') AS 'days',
IF(meta_key='playstyle',meta_value,'') AS 'playstyle',
IF(meta_key='raid-progression',meta_value,'') AS 'progression',
IF(meta_key='raid-time',meta_value,'') AS 'time',
IF(meta_key='tanker-spot',meta_value,'') AS 'tank',
IF(meta_key='healer-spot',meta_value,'') AS 'healer',
IF(meta_key='melee-dps-spot',meta_value,'') AS 'melee',
IF(meta_key='ranged-dps-spot',meta_value,'') AS 'ranged',
IF(meta_key='magic-dps-spot',meta_value,'') AS 'magic',
IF(meta_key='ilvl',meta_value,'') AS 'ilvl',
IF(meta_key='voicechat',meta_value,'') AS 'voice',
IF(meta_key='voicechatpass',meta_value,'') AS 'voicep',
FROM wpstatic_bp_groups_groupmeta
The point is, I need to merge that result (view) so all the group_id=1 or 2 or 3, etc stand in one single row, like this:
gid| time-zone| playstyle | main-raid
--------------------------------------------
1 |Kwajalein | hardcore | Final Coil of Bahamut
2 |SaoPaulo | regular | Second Coil of Bahamut
etc
Can anyone help me there?
Just surround your IFs in a MAX, or another aggregate function that will capture the non-empty strings (e.g., GROUP_CONCAT), and add a GROUP BY group_id add the end. For example,
SELECT
group_id AS gid,
MAX(IF(meta_key='recruiting-status',meta_value,'')) AS 'Recruitment',
MAX(IF(meta_key='server',meta_value,'')) AS 'server',
...
FROM wpstatic_bp_groups_groupmeta
GROUP BY group_id
I have two tables that have these columns (I'm only showing revelant ones) :
tasks table
+----+------+
| id | todo |
+----+------+
| 1 | 0 |
| 2 | 1 |
| 3 | 1 |
| 4 | 0 |
| 5 | 1 |
+----+------+
entries table
+----+---------+---------+------+
| id | task_id | user_id | done |
+----+---------+---------+------+
| 1 | 3 | 1 | 1 |
| 2 | 5 | 2 | 1 |
| 3 | 5 | 1 | 1 |
| 4 | 2 | 1 | 0 |
+----+---------+---------+------+
I query these tables and only keep tasks where todo = 1, So I already have the data in a PHP object.
I then have two lists that the user can view : tasks that are to do, and archived (done), and tasks that are to do. I can generate the first list just fine, I'm looping through each task and entries if they have a matching task_id where user_id == $loggeduser && done == 1, and unsetting the index of those that don't match. However, I cannot find a logic to do this with my archive list, as I don't have entries to match. How do I loop my tasks and only keep those that are done, for the user? In this case, for the archive list for user 1, I'm excepting to only keep task id 3 and 5, and for user 2, only keep task id 2.
Thanks.
You can do all this using plain SQL (I suppose you're using some relational database).
This query gives you all the tasks "todo & done". To get the tasks "todo & not done", just change the "e.done = 1" to "e.done = 0". I'm sure you get the idea.
SELECT * FROM tasks t
INNER JOIN entries e ON t.id = e.task_id
AND e.user_id = [logged_user_id]
AND e.done = 1
WHERE
t.todo = 0
I have 3 table:
tblNames:
| id | firstname | lastname |
+------+---------------+--------------+
| 1 | John | Smith |
tblJosbs (this table accepts multiple checkbox value at the same time):
| id | jobs |
+------+-----------------------+
| 1 | Nurse |
+------+-----------------------+
| 2 | Call Center Agent |
+------+-----------------------+
| 3 | Police |
tblNamesJobs (this table is used to JOIN the other 2 tables):
| id | name_id | jobs_id |
+------+-------------+-------------+
| 1 | 1 | 1 |
+------+-------------+-------------+
| 2 | 1 | 2 |
+------+-------------+-------------+
| 3 | 1 | 3 |
All is fine but can someone show me the INSERT statement for the 3rd table I should use to when I will add new information?
For example add record that John Smith is a Call Center Agent
insert into tblNamesJobs (name_id,jobs_id )
values (
select id from tblNames where
firstname='John'
and lastname='Smith' limit 1
,
select id from tblJosbs where jobs='Call Center Agent' limit 1
);
If you are already depending on tauto increment..you can get the lastinserid, depending on your adapter.
eg. mysql_insert_id
for PDO we can use --PDO::lastInsertId.
so you will have id's of earlier inserted tables, that you can save in the new one.
INSERT INTO tblNamesJobs (name_id, jobs_id) VALUES (XXXX,YYYY)
That is assuming the table's id is auto-incrementing.
It should be noted that both the name_id and jobs_id columns in the "joiner" table should be foreign keys to the respective columns in the other table.
Edit - Valex's answer goes into more detail about what to do if you don't already have the id values.
If possible, I would recommend using some sort of framework that would handle the "joiner" table for you.