why query unable to select specific id using mysqli? - php

I am facing a strange issue using mysqli table. I have table in which there are some records. I am executing following query
SELECT * FROM `users` WHERE
EXISTS (
SELECT *
FROM `games`
INNER JOIN `users_games`
ON `games`.`id` = `users_games`.`game_id`
WHERE
`users`.`id` = `users_games`.`user_id` AND `game_id` = 10
)
AND `users`.`id` = 10
LIMIT 12;
In users_games table there are 11 rows (1 to 13) for user_id = 10. The above query is working for all
`game_id` = 10 /* it is working */
When I try to execute game_id= 1 then it is not showing record however there is row for game_id= 1.
Below it users_games table

Given these users
select id from users;
+-----+
| id |
+-----+
| 1 |
| 2 |
| 3 |
| 6 |
| 7 |
| 8 |
| 10 |
| 12 |
| 14 |
| 15 |
| 16 |
| 17 |
| 999 |
+-----+
and this
drop table if exists games,users_games;
create table games (id int);
create table users_games(user_id int, game_id int);
insert into games values (1),(10);
insert into users_games values(1,1),(1,10),(10,1);
Your query
SELECT id FROM `users` WHERE
EXISTS (
SELECT *
FROM `games`
INNER JOIN `users_games`
ON `games`.`id` = `users_games`.`game_id`
WHERE
`users`.`id` = `users_games`.`user_id` AND `game_id` = 1
)
AND `users`.`id` = 10;
Produces
+----+
| id |
+----+
| 10 |
+----+
1 row in set (0.00 sec)
Which is as expected. You could improve your question by including sample data as text in the question (as I have done in the answer).

Related

Effective way to update more than 10000 row of data in MYSQL?

I would like to query a table, and update the table data, but the data more than 10000 row, so what is the effective way? My code now:
+----+------+---------+
|id | rank | newrank |
+----------------------+
|1 | 1 | 0 |
|2.. | 2 | 0 |
|10000| 10000| 0 |
+----------------------+
//I am using a template language, DB::query = mysqli_query
$ss = DB::fetch_all("SELECT *FROM t1 ORDER BY rank ASC");
$x = 1;
foreach($ss as $sr){
DB::query("UPDATE t1 SET newrank = '".$x."' WHERE id = '".$sr['id']."'");
$x++;
}
Currently I am using the above code, but if data have to update in huge amount, the server will be crashed. Thank you.
You don't need to fetch anything to do this, and you don't need to worry about the number of rows.*
Given a test table:
create table t1 (id int primary key, `rank` int);
insert into t1 values (1, null), (2, null), (3, null), (6, null);
Solution for MySQL 5.x:
set #r = 0;
update t1 set `rank` = (#r := #r + 1) order by `rank`, id;
select * from t1;
+----+------+
| id | rank |
+----+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 6 | 4 |
+----+------+
Here's a better solution for MySQL 8.0 using a window function. I'll rank them in the descending order, just so we can see that it changed the ranks:
with cte as ( select id, rank() over (order by `rank` desc, id desc) as r from t1 )
update cte join t1 using (id) set t1.`rank` = cte.r;
select * from t1;
+----+------+
| id | rank |
+----+------+
| 1 | 4 |
| 2 | 3 |
| 3 | 2 |
| 6 | 1 |
+----+------+
Note: I had to delimit rank with back-ticks because rank is a reserved keyword in MSQL 8.0.
* Eventually, you do need to worry about the number of rows. MySQL has a limit on the total size of a transaction, if the binary log is enabled. The total size of rows modified during a transaction is limited by the option max_binlog_cache_size. Not the number of rows, but the size of the rows in bytes.

Delete all rows, except last 10 for each client that has related row(s) in the table in one query?

So my situation is this:
Clients table - has client data etc, not too exciting
Recently Viewed table - table that has recently viewed things for the client(s), And has structure like this:
( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, client_id INT NOT NULL
, cookie_user_id INT NOT NULL
, hotel_id INT NOT NULL
, added DATETIME NOT NULL
, comment TEXT
,status TINYINT NOT NULL DEFAULE 1
);
I currently have a partially working SQL to delete rows in the recently viewed table that right now globally limits number of latest remaining undeleted records in it. This is how it looks like now
DELETE FROM `recently_viewed`
WHERE `recently_viewed`.`id` NOT IN (
SELECT id
FROM (
SELECT `id`
FROM `recently_viewed`
WHERE `client_id` IN (SELECT `id` FROM `klijenti`)
ORDER BY `id` DESC
LIMIT 5
) x
)
AND `client_id` <> 0
"LIMIT 5" part should limit to the N records to remain in recently viewed table on a "per client" basis. Right now it limits records in recently viewed table to 5 no matter how many clients actually have records there. So if I have 10 clients, each of them has 8 records in that table, I would like this query to delete as many oldest records as needed to leave only 5 newest recently viewed items for EACH client and not just leave 5 overall in the table, ignoring the "per each client" logic. Hope that makes sense to you :)
Currently, this query would be ok if I would first fetch all clients in the app and then do a foreach loop to make another query for each client and leave 5 of his latest recently viewed items, but would like to do this in one SQL query instead.
How could this be done ? Thank you
You can do it like this:
DELETE FROM `recently_viewed`
WHERE `recently_viewed`.`id` NOT IN (
SELECT id
FROM (
SELECT t.`id`,count(*) as rnk
FROM `recently_viewed` t
INNER JOIN `recently_viewed` s
ON(t.`client_id` = s.`client_id` and t.added <= s.added)
WHERE t.`client_id` IN (SELECT `id` FROM `klijenti`)
GROUP BY t.`ID`
) x
WHERE rnk <= 5
)
AND `client_id` <> 0
You can use vartiables to in order to count the 5 more recent records per client_id:
DELETE FROM `recently_viewed`
WHERE `recently_viewed`.`id` NOT IN
(
SELECT id
FROM (
SELECT `id`,
#rn := IF(#cid = `client_id`, #rn + 1,
IF(#cid := `client_id`, 1, 1)) AS rn
FROM `recently_viewed`
CROSS JOIN (SELECT #rn := 0, #cid := 0) AS vars
WHERE `client_id` IN (SELECT `id` FROM `klijenti`)
ORDER BY `client_id`, `id` DESC) x
WHERE x.rn <= 5
)
Giorgos's answer is faster, but here's another method...
Consider the following...
SELECT * FROM my_table ORDER BY x,i;
+---+------+
| i | x |
+---+------+
| 2 | A |
| 3 | A |
| 6 | A |
| 8 | A |
| 1 | B |
| 5 | B |
| 4 | C |
| 7 | C |
| 9 | C |
+---+------+
Let's say we want to select the two latest i for each x. Here's one way to do that...
SELECT m.* FROM my_table m JOIN my_table n ON n.x = m.x AND n.i >= m.i GROUP BY m.i HAVING COUNT(*) <= 2;
+---+------+
| i | x |
+---+------+
| 1 | B |
| 5 | B |
| 6 | A |
| 7 | C |
| 8 | A |
| 9 | C |
+---+------+
The inverse of this set can be found as follows....
SELECT m.* FROM my_table m JOIN my_table n ON n.x = m.x AND n.i >= m.i GROUP BY m.i HAVING COUNT(*) > 2;
+---+------+
| i | x |
+---+------+
| 2 | A |
| 3 | A |
| 4 | C |
+---+------+
...which in turn can be incorporated in a DELETE. Here's a crude method for doing that...
DELETE a FROM my_table a
JOIN
( SELECT m.* FROM my_table m JOIN my_table n ON n.x = m.x AND n.i >= m.i GROUP BY m.i HAVING COUNT(*) > 2 ) b
ON b.i = a.i;
Query OK, 3 rows affected (0.03 sec)
SELECT * FROM my_table ORDER BY x,i;
+---+------+
| i | x |
+---+------+
| 6 | A |
| 8 | A |
| 1 | B |
| 5 | B |
| 7 | C |
| 9 | C |
+---+------+
As I say, if performance is critical, then look at a solution along the lines that Giorgos has provided.

MYSQL get row rank

I have a mysql table and I need to get random row and get the rank of total view
+--------+------------+---------+
| id | name |totalview|
+--------+------------+---------+
| 1 | ex1 | 20 |
| 2 | ex2 | 100 |
| 3 | ex3 | 30 |
| 4 | ex4 | 40 |
+--------+------------+---------+
for example :
SELECT * FROM `table` WHERE `id` = '$rand';
$rand may be 1 or 2 etc ..
I need to get rank of this row by totalview
thank's
SELECT *,
(SELECT COUNT(*) FROM table t2 WHERE totalview > t1.totalview ) + 1 cnt
FROM table t1
WHERE id = '$rand';
SELECT SUM(ref.totalview < t.totalview) FROM t1 CROSS JOIN t1 ref WHERE t1.id = '$rand'

MySQL signed Order By on integer zero issue

I currently have a signed integer set in a MyISAM table,
The query is like:
SELECT * from some_view ORDER BY value DESC, dateAdded DESC
I'm getting this order:
1
2
-1
-2
0
0
The zero values aren't seen as +/- but I need them to be greater than a negative value!!!
This is my actual query:
SELECT * FROM vw_answer sa left join vw_answer_votes av on av.answerID = sa.id WHERE sa.id = 77 ORDER BY av.vote, dateAdded
I have the following:
tbl_users
tbl_solutions - Indexes tbl_users.id as authorID
tbl_solution_answers - Indexes tbl_solutions.id as solutionID
tbl_solution_answer_votes - Indexes tbl_users.id as voterID and tbl_solution_answers
as answerID
tbl_solution_answer_votes only has one none indexed column which contains -1 or 1 depending on the vote cast by a user. This is a signed integer.
my view when I select answers selects tbl_solution_answers and the sum(tbl_solution_answer_votes.vote)
It all works, except the ordering on signed integer.
Edit: Values in the votes table only exist if a user has voted else it simply doesn't exist. I need something like this I think:
SELECT * FROM tbl_answers sa right join vw_answer_votes av on av.answerID = sa.id
ORDER BY av.vote > 0 desc, av.vote IS NULL, av.vote < 0 desc, dateAdded DESC
You are ordering on two fields, value and dateadded, try taking out your date added.
create table order_by_neg_test
(`id` int(11) unsigned not null auto_increment,
`value` int(11) signed not null,
primary key (`id`)) engine=myisam default charset=utf8;
I inserted some random values and here are the results:
# No order
mysql> select * from order_by_neg_test;
+----+-------+
| id | value |
+----+-------+
| 1 | 45 |
| 2 | 1 |
| 3 | 0 |
| 4 | -1 |
| 5 | 17 |
| 6 | 27 |
| 7 | -1 |
+----+-------+
7 rows in set (0.00 sec)
# With an order
mysql> select * from order_by_neg_test order by value desc;
+----+-------+
| id | value |
+----+-------+
| 1 | 45 |
| 6 | 27 |
| 5 | 17 |
| 2 | 1 |
| 3 | 0 |
| 4 | -1 |
| 7 | -1 |
+----+-------+
7 rows in set (0.00 sec)
Notice in the order, 0 > -1.
You should try
ORDER BY CAST(value AS SIGNED) DESC
if your view screwed up the type

Contitionally structured and ordered MySQL query

I have a table in a MySQL Database.
It is structured as such:
CREATE TABLE `wall` (
`wall_id` int(10) NOT NULL auto_increment,
`user_id` int(10) NOT NULL,
`wall_content` varchar(1024) NOT NULL,
`time_posted` varchar(64) NOT NULL,
`is_reply` int(10) NOT NULL,
PRIMARY KEY (`wall_id`)
) ENGINE=MyISAM
The column 'is_reply' will be the id of 'wall_id' to which it is a reply of. How would I structure a query to get all the rows based on an inner join of another table to cross reference the user_id, and to group the wall posts with the comments below it whilst ordering the wall posts by 'time_posted'
My current query does that without grouping the comments. It is:
SELECT wall.*, user_wall.*, users.username, users.avatar_id
FROM `wall`
INNER JOIN user_wall ON user_wall.wall_id = wall.wall_id
INNER JOIN users ON users.user_id = wall.user_id
WHERE user_wall.user_id=15
I hope you can understand this.
Edit:
The table 'user_wall' is a table that stores what values are on the users wall, and the 'wall' table stores what is actually posted. The user_id in the 'wall' table is a reference to who posted that post.
The current query as stated above is fully functional and returns data as such:
wall_id | user_id | wall_content | time_posted | is_reply | user_id | wall_id | username | avatar_id
1 | 1 | *content* | *time* | 0 | 2 | 1 | User1 | 1
2 | 1 | *content2* | *time2* | 0 | 2 | 2 | User1 | 1
3 | 1 | *content3* | *time3* | 1 | 1 | 3 | User1 | 1
Whereas my question is, how do you structure the query so the result is like so:
wall_id | user_id | wall_content | time_posted | is_reply | user_id | wall_id | username | avatar_id
1 | 1 | *content* | *time* | 0 | 2 | 1 | User1 | 1
3 | 1 | *content3* | *time3* | 1 | 1 | 3 | User1 | 1
2 | 1 | *content2* | *time2* | 0 | 2 | 2 | User1 | 1
Where the row with 'wall_id' 3 which has and 'is_reply' of 1 to be beneath the row with 'wall_id'. Similarly a row with an 'is_reply' of 2 will be under the row with the row with a 'wall_id' of 2.
Now that you've edited it I understand what you mean. This should do it:
ORDER BY IF(wall.is_reply, wall.is_reply, wall.wall_id), wall.wall_id
Format: IF(EXPRESSION, IF_TRUE, IF_FALSE)
SQL can't return multiple rows from one table (e.g. the wall_comments) and only one from the ones it is joined with. In other words, that can't be done. There is an alternative that will get the same results but use two SQL queries and some PHP code.
Query #1:
SELECT wall_comments.*
FROM `wall_comments`
INNER JOIN user_wall ON wall_comments.wall_id = user_wall.wall_id
WHERE user_wall.user_id=15
Query #2:
SELECT wall.*, user_wall.*, users.username, users.avatar_id
FROM `wall`
INNER JOIN user_wall ON user_wall.wall_id = wall.wall_id
INNER JOIN users ON users.user_id = wall.user_id
WHERE user_wall.user_id=15
PHP:
<?php
$result1 = mysql_query($query1);
$result2 = mysql_query($query2);
$comments = array();
while($row = mysql_fetch_assoc($result1))
{
$comments[$row['wall_id']][] = $row;
}
$walls = array();
while($row = mysql_fetch_assoc($result2))
{
$walls[] = array_merge(
$row,
array(
'comments' => isset($comments[$row['wall_id']]) ? $comments[$row['wall_id']] : array(),
),
);
}
?>

Categories