Select N rows before and after given row with condition - php

id title cat lang
6101 AAba 1 1
6102 AAad 4 2
6103 AAaf 4 2
6104 AAa 1 1
6105 AAtar 4 2
6106 AAao 1 1
6107 ABya 4 2
6108 ABar 4 2
6109 ACar 1 2
6110 BCad 3 1
6111 CCas 4 1
6112 DCas 4 2
6113 GCaz 2 2
6114 FCaew 2 2
6115 FCaw3 4 2
6116 FCa3 1 1
6117 FCa4 4 2
6118 FCa5 2 1
6119 FCa6 4 2 --- last id
Table news, id is primary key, title - is title field,
cat - there are four types of categories of news, lang - language code 1 or 2.
I want to achieve following:
1) Case Edit news - id is available - for example 6111, lang is 1, cat is 4
I want to collect these id and titles:
6119
6117
6115
6112
6109
6105
6103
6102
which are the opposite language - lang is 2, the same category cat is 4 and
within range of 4 (or n) id before and after given id 6111
also ordered desc
2) Case Add news - id is not available, or after insert in php is mysql_insert_id()
the same functionality - last 10 id with the same cat = 4 and the opposite lang = 2
So far I have tried this:
for case 1) php snippet:
$id = 6111;
$lang = 1;
$tip = 4;
$sql = "SELECT id, title
FROM pubs
WHERE id BETWEEN ".($id-4)." AND ".($id+4). " AND
tip = ".$tip." AND
lang <> ".$lang."
ORDER BY id DESC ";
BUT this gets 4 id before and after the id, not from the collection of id I need .
for case 2)
$sql = "SELECT id, title
FROM news
WHERE cat = 4 AND
lang <> 1
ORDER BY id DESC LIMIT 10";
This seem to be working, but may be I'm wrong.
Is there any way to achieve Case 1 this with php and mysql?
Better way for case 2?

Given that you only have the $id, this horrible looking beast is the best I can come up with. I'm fairly sure there's a tidier way to do it, but I think this will work (FIXED)
$id = 6111;
$query = "
SELECT `id`, `title`
FROM `news`
WHERE `id` IN (
SELECT * FROM (
SELECT `id`
FROM `news`
WHERE `cat` = (
SELECT `cat`
FROM `news`
WHERE `id` = ".$id."
) AND `lang` != (
SELECT `lang`
FROM `news`
WHERE `id` = ".$id."
) AND `id` < ".$id."
ORDER BY `id` DESC
LIMIT 4
) `lower`
) OR `id` IN (
SELECT * FROM (
SELECT `id`
FROM `news`
WHERE `cat` = (
SELECT `cat`
FROM `news`
WHERE `id` = ".$id."
) AND `lang` != (
SELECT `lang`
FROM `news`
WHERE `id` = ".$id."
) AND `id` > ".$id."
ORDER BY `id` ASC
LIMIT 4
) `upper`
)
ORDER BY `id` DESC
";

Something like this might work:
$sql = "SELECT id, title FROM pubs
WHERE tip = ".$tip." AND lang <> ".$lang."
ORDER BY id DESC
LIMIT ".($id-4).",".($id+4);
There may be a more elegant solution, but this gets the job done.

Related

Retriving 3 last records from database for each id

I want to retrive last three posts from database based on category id, e.g
post_id category_id
1 1
2 4
3 3
4 4
5 2
6 1
7 3
8 2
9 4
10 1
11 2
12 3
and I want to retrive something like this
post_id category_id
1 1
6 1
10 1
5 2
8 2
11 2
7 3
3 3
12 3
4 4
2 4
9 4
I have made this query but how to limit to return 3 of each category_id?
SELECT
`posts`.`id`,
`posts`.`title`,
`posts`.`slug`,
`posts`.`image`,
`posts`.`status`
FROM
`posts`
ORDER BY
`posts`.`id`
How to limit to return 3 records for each category_id, when I use LIMIT 3 query returns only 3 records.
You can read 3 new post_id from each category_id
SELECT
p1.`post_id`,
p1.`category_id`
FROM
post p1
JOIN post p2 ON p1.`category_id` = p2.`category_id`
AND p2.`post_id` >= p1.`post_id`
GROUP BY
p1.`post_id`,
p1.`category_id`
HAVING
COUNT(*) <= 3
ORDER BY
`category_id`,
`post_id`
SQL FIDDLE : LIVE DE
$sql = "SELECT `posts`.* FROM `posts` WHERE `posts`.`category_id` = 1 ORDER BY `posts`.`id` DESC LIMIT 3"
run it then
$sql = "SELECT `posts`.* FROM `posts` WHERE `posts`.`category_id` = 2 ORDER BY `posts`.`id` DESC LIMIT 3"
run that then
$sql = "SELECT `posts`.* FROM `posts` WHERE `posts`.`category_id` = 3 ORDER BY `posts`.`id` DESC LIMIT 3"
then that, but don't forget
$sql = "SELECT `posts`.* FROM `posts` WHERE `posts`.`category_id` = 4 ORDER BY `posts`.`id` DESC LIMIT 3"`
then you have reached your destination
it is better to just run the queries separate.

PHP/Mysql: 2 selects from same table in one query

This is how my table looks like:
id | name | value
-----------------
1 | user1| 1
2 | user2| 1
3 | user3| 3
4 | user4| 8
5 | user5| 6
6 | user7| 4
7 | user8| 9
8 | user9| 2
What I want to do is to select all the other users, in one query, who's value is user1's value lower than it's value plus 3, higher than it's value minus 3 or equal to it's value.
Something like this:
$result = mysqli_query($con,"SELECT * FROM users WHERE value<'4' OR value>'-2'") or die("Error: ".mysqli_error($con));
while($row = mysqli_fetch_array($result))
{
echo $row['name'].'<br/>';
}
The problem is that users1's value can vary every time the query is run.
Sorry for lame names, but this should work:
NOTE: I named table with your data as "st".
SELECT b.user, a.value as "user1val", b.value as "otheruservalue" FROM st as a
join st as b
on a.user = "user1" and a.user != b.user
where
(b.value > (a.value - 3)) and (b.value < (a.value + 3))
We get unique pairs of user1's value and other user's value by joining same table. After that we just do some simple comparison to filter rows with suitable values.
$user1 = mysql_fetch_assoc(mysql_query("SELECT `value` FROM `users` WHERE id='1'"));
$result = mysql_query("SELECT * FROM `users` WHERE value<'".$user1['value']."+3' OR value>'".$user1['value']."-3'");
Or nested queries :
$result = mysqli_query($con, "select * from `users` where `value` < (select `value` from `users` where `name`='user1')+3 OR `value` > (select `value` from `users` where `name`='user1')-3");

MySQL Request from two tables advanced

I have request from my MySQL table. It looks like below:
SELECT s . * , if( v.ip IS NULL , 0, 1 ) AS voted, v.vote
FROM `emvc4_records` s
LEFT JOIN `emvc4_records_votes` v ON ( s.id = v.record_id
AND v.day = CURRENT_DATE
AND v.ip = '2130706433' )
WHERE 1 =1
AND `category` =2
AND `caption` LIKE '%My%'
OR `description` LIKE '%My%'
ORDER BY `rating` DESC
LIMIT 0 , 25
I get this result:
id category category_name caption description created postby votes_up votes_down rating status have_voted vote
5 1 My second idea My second idea 2013-06-04 00:00:00 102 2 0 2 0 0 NULL
7 2 My first question My first question 2013-06-04 00:00:00 102 2 0 2 0 0 NULL
9 2 My second My second question 2013-06-04 00:00:00 102 1 0 1 0 0 NULL
My question: In my sql query I have: "AND category =2". So why I get record id=5 (it has category= 1) in my output?
Thanks!
The conditions in the WHERE clause are not properly organized. Add parentheses to group the conditions that you want. I guess LIKE '%My%' should be grouped.
SELECT s . *
, if( v.ip IS NULL , 0, 1 ) AS voted
, v.vote
FROM `emvc4_records` s
LEFT JOIN `emvc4_records_votes` v ON ( s.id = v.record_id
AND v.day = CURRENT_DATE
AND v.ip = '2130706433' )
WHERE 1 = 1
AND `category` = 2
AND (`caption` LIKE '%My%' OR `description` LIKE '%My%')
ORDER BY `rating` DESC
LIMIT 0 , 25
I don't why you added 1 = 1, but you can remove that.
Your query should be look like:
...............
`category` =2
AND (`caption` LIKE '%My%' OR `description` LIKE '%My%')
.................

Looping through MySQL Results

I'm not sure exactly how this is called but I'll try to describe as good as I can what I want to acheive.
So, first of all, there is a variable, called $id, which is actually $_GET['id']. Assuming the user is entering the following page by requesting: /page.php?id=6. Now what I need to do is to provide the information about the next 3 pages from database. Here is the database:
TABLE `pages`
id | page_name
______________________
1 | AAAAA
2 | BBBBB
3 | CCCCC
4 | DDDDD
5 | EEEEE
6 | FFFFF
7 | GGGGG
8 | HHHHH
9 | IIIII
So, while requesting the page with id 6, the following script returns the next 3 pages (7,8,9):
$res = mysql_query("SELECT * FROM `pages` WHERE `id`>'".intval($id)."' ORDER BY `id` DESC LIMIT 3");
while($arr = mysql_fetch_assoc($res))
{
print("Page ID: ".$arr['id']."; Page Name: ".$arr['page_name']."\n");
}
And here is the output:
Page ID: 7; Page Name: GGGGG
Page ID: 8; Page Name: HHHHH
Page ID: 9; Page Name: IIIII
And it works fine until the $id is greater then 6. When it is (/page.php?id={7/8/9}), the output doesn't show 3 pages any more, but 2 pages, 1 page and respectively no output when $id is 9.
So my question is: Is there a way to go back and start from the beginning when there are not enough results (less than 3) to display?
When accessing /page.php?id=8, the output should contain pages with id 9, 1 and 2.
When accessing /page.php?id=9, the output should contain pages with id 1, 2, 3.
When accessing /page.php?id=3, the output should contain pages with id 4, 5, 6 and so on.
(SELECT *, 0 AS custom_order FROM `pages` WHERE `id`>'".intval($id)."' ORDER BY `id` ASC LIMIT 3)
UNION ALL
(SELECT *, 1 AS custom_order FROM `pages` ORDER BY `id` ASC LIMIT 3)
ORDER BY custom_order, id ASC
LIMIT 3
This way you always get 3 pages. If not enough next pages, you will get up to 3 from the beginning.
You could modify the query to be something like:
select * from
(select *, id-$inval($id) as order_by
from pages were id > $inval($id) order by id asc limit 3
union
select *, id as order_by
from pages order by id asc limit 3 ) as pages
order by order_by asc
I would solve this way (one possible issue is that the resultset could contain at most 6 records instead of 3):
$res = mysql_query("(SELECT * FROM `pages` WHERE `id`>'".intval($id)."' ORDER BY `id` ASC LIMIT 3) UNION DISTINCT (SELECT * FROM `pages` WHERE id>0 ORDER BY id ASC LIMIT 3)");
$counter = 0;
while($arr = mysql_fetch_assoc($res) && $counter<3)
{
$counter++;
print("Page ID: ".$arr['id']."; Page Name: ".$arr['page_name']."\n");
}

Order by ID if two users have same number of credits

:-)
I have this script, which find a users position taken from the number of credits.
It all works, but i have a little problem. If two users have the same credits, both of them will be on the same position.
Can I do, so if there are more users with same credits, then the system need to order by the users ID and out from that give them a position?
This is my code so far:
$sql = "SELECT COUNT(*) + 1 AS `number`
FROM `users`
WHERE `penge` >
(SELECT `penge` FROM `users`
WHERE `facebook_id` = ".$facebook_uid.")";
$query_rang = $this->db->query($sql);
So if i have this:
ID -------- Credits
1 -------- 100
2 -------- 100
3 -------- 120
Then the rank list should be like this:
Number 1 is user with ID 3
Number 2 is user with ID 1
Number 3 is user with ID 2
ORDER BY credits DESC, id ASC. This will sort by credits and break ties with the id.
UPDATE
I understand now that you want the ranking information for the user, not just to sort the users by credits and ids. This will give you the complete list of users and their rankings:
SELECT #rank:=#rank+1 AS rank, users.id, users.facebook_id FROM users, (SELECT #rank:=0) dummy ORDER BY penge DESC, id ASC
Getting the row number is the tricky bit solved by this blog post:
http://jimmod.com/blog/2008/09/displaying-row-number-rownum-in-mysql/
$sql = "SELECT COUNT(*) + 1 AS `number` FROM `users` WHERE `penge` > (SELECT `penge` FROM `users` WHERE `facebook_id` = ".$facebook_uid.") ORDER BY COUNT(*) + 1 desc, users.ID";
$query_rang = $this->db->query($sql);
Later EDIT:
I don't understand why you still have the same results....
I made a quick test. I have created a table:
Test: ID (Integer) and No (Integer)
I have inserted some values:
id no
1 1
1 1
1 1
2 1
3 1
4 1
4 1
5 1
Now, if I run:
SELECT
id, COUNT(*) + 1 AS `number`
FROM
test
GROUP BY
id
I get:
id number
1 4
2 2
3 2
4 3
5 2
But if I add ORDER BY:
SELECT
id, COUNT(*) + 1 AS `number`
FROM
test
GROUP BY
id
ORDER BY
count(*) desc, id
then I get:
id number
1 4
4 3
2 2
3 2
5 2

Categories