MySQL signed Order By on integer zero issue - php

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

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.

why query unable to select specific id using mysqli?

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).

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-query other table condition PHP

I have this table in mysql called safespot
+---------+---------------+
| term_id | userid | safe |
+---------+--------|------+
| 1 | 1 | large number, unix timestamp here
| 1 | 2 | large number, unix timestamp here
| 1 | 3 | large number, unix timestamp here
| 1 | 4 | large number, unix timestamp here
+---------+--------+
And this is table users:
+----+-------------+-------------+
| id | userid | cash |
+----+-------------+-------------+
| 1 | 1 | 100000 |
| 2 | 2 | 100000 |
| 3 | 3 | 100000 |
+----+-------------+-------------+
how can i do something like
SELECT * FROM `users` where `userid`=1 and `cash`>= 1000 and " userid do not exist in table safespot" or "if the user exists in the safestop table, check if the current timestamp is higher than the safe colum)
So basically do a query that also would return it if userid dont exist in safespot table, or if it does, that timestamp is higher than safe_value.
SELECT * FROM users u
LEFT JOIN safespot s ON s.userid = u.userid
WHERE
u.userid = 1
AND u.cash = 1000
AND (s.userid IS NULL OR s.safe > UNIX_TIMESTAMP())
This returns users where
there is no entry in safespot for the given userid, or
there is an entry in safespot with the value of safe greater than the current timestamp.
SELECT * FROM `users` WHERE `userid`=1 AND `cash`>= 1000 AND (userid NOT IN (
SELECT DISTINCT userid FROM safespot
) OR (userid IN (
SELECT DISTINCT userid FROM safespot WHERE safe < UNIX_TIMESTAMP()
)
)
Use WHERE NOT EXISTS like
SELECT u.* FROM `users` u
where u.`userid`=1
and u.`cash` >= 1000
and ( NOT EXISTS ( select 1 from safespot where userid <> u.userid)
or EXISTS (select 1 from safestop where userid = u.userid and safe < CURRENT_TIMESTAMP));

MySQL: Get all entries in a specific time frame with value=0 unless there's an entry in the same time frame with value=1

Table payment_transaction
+----+---------+--------+------------+
| ID | user_id | status | time_stamp |
+----+---------+--------+------------+
| 1 | 1 | 1 | 1414541884 |
| 2 | 2 | 0 | 1414576722 |
| 3 | 2 | 0 | 1414577273 |
| 4 | 3 | 0 | 1414782966 |
| 5 | 3 | 1 | 1414785691 |
| 6 | 4 | 0 | 1415112933 |
+----+---------+--------+------------+
This table stores all payment transactions. status = 0 means the payment failed for whichever reason, status = 1 means the payment was successful.
I'd now like to setup a cronjob, where all users will get a message who weren't able to buy a membership in the last 7 days. As in the example table above, I only need 2 rows with the user_id, transaction_id of the most recent payment and time_stampe of the most recent payment. As user_id = 3 managed to get a membership after the first failed payment, he should be excluded:
+---------+----------------+------------+
| user_id | transaction_id | time_stamp |
+---------+----------------+------------+
| 2 | 3 | 1414577273 |
| 4 | 6 | 1415112933 |
+---------+----------------+------------+
Is it possible to get all this done in one query? And if yes, is it more effective than using two queries?
What I've got so far:
SELECT DISTINCT
`t`.`user_id`,
`t`.`id` AS `transaction_id`,
`t`.`time_stamp`
FROM `payment_transaction` AS `t`
WHERE
`t`.`status` = 0
AND `t`.`time_stamp` < UNIX_TIMESTAMP() - 60*60*24*7
ORDER BY
`t`.`id`
However, it doesn't exclude the user with user_id = 3 and also gives me the first failed payment instead the most recent.
---- EDIT ----
Thanks to RST for providing a solution in the comments below. After some additional changes, the final query looks like this:
SELECT
`t`.`user_id`,
MAX(`t`.`id`) AS `transaction_id`,
MAX(`t`.`time_stamp`) AS `time_stamp`
FROM `payment_transaction` AS `t`
WHERE
`t`.`status` = 0
AND `t`.`time_stamp` < UNIX_TIMESTAMP() - 60*60*24*7
AND `t`.`user_id` NOT IN (
SELECT `user_id`
FROM `payment_transaction`
WHERE `status` = 1
AND UNIX_TIMESTAMP(`time_stamp`) < UNIX_TIMESTAMP() - 60*60*24*7
)
GROUP BY
`t`.`user_id`
ORDER BY
`t`.`id`
Thinking a little away from your request but should still get the same results, depending on how you've coded your application. Is the assumption that the latest transaction will be the most relevant an accurate one?
If so, you could look at something like the following (untested):
SELECT DISTINCT
`t`.`user_id`,
`t`.`id` AS `transaction_id`,
`t`.`time_stamp`
FROM `payment_transaction` AS `t`
WHERE
`t`.`time_stamp` < UNIX_TIMESTAMP() - 60*60*24*7
GROUP BY
`t`.`user_id`
ORDER BY
`t`.`id`
Please note I've not tested this SQL, as I'm away from my dev machine. I'd recommend having a look through the intricacies of the GROUP BY command to make sure you're not going to have some edge cases that aren't caught.
DROP TABLE IF EXISTS payment_transaction;
CREATE TABLE payment_transaction
(ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,user_id INT NOT NULL
,status INT NOT NULL
,time_stamp BIGINT NOT NULL
);
INSERT INTO payment_transaction VALUES
(1 ,1 ,1 ,1414541884),
(2 ,2 ,0 ,1414576722),
(3 ,2 ,0 ,1414577273),
(4 ,3 ,0 ,1414782966),
(5 ,3 ,1 ,1414785691),
(6 ,4 ,0 ,1415112933);
SELECT x.*
FROM payment_transaction x
JOIN
( SELECT a.user_id
, MAX(a.id) max_id
FROM payment_transaction a
LEFT
JOIN payment_transaction b
ON b.user_id = a.user_id
AND b.status = 1
AND b.time_stamp > a.time_stamp
WHERE a.time_stamp > UNIX_TIMESTAMP(NOW())-604800
AND a.status = 0
AND b.id IS NULL
) y
ON y.user_id = x.user_id
AND y.max_id = x.id;
+----+---------+--------+------------+
| ID | user_id | status | time_stamp |
+----+---------+--------+------------+
| 6 | 4 | 0 | 1415112933 |
+----+---------+--------+------------+

Categories