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

Related

Mysql query returns duplicate rows when joining tables

I have a table that stores user_avatar filepath :
id | userid | file | last_update |
1 | 23 | myphoto.png | x_timestamp |
2 | 23 | myphoto2.png | x_timestamp |
3 | 25 | myavatar.png | x_timestamp |
I have a table that returns user_score :
id | gamerid | score | last_update |
1 | 23 | 44 | x_timestamp |
2 | 25 | 99 | x_timestamp |
This is the query I use to display user scores with their avatar
SELECT * FROM user_score us LEFT JOIN user_avatar ua ON us.gamerid=ua.userid
Above query will return 2 duplicate rows for user 23 because he as downloaded 2 different photos in the user_avatar table. Right now it outputs like this:
GAMER SCORES RESULTS
myphoto.png | 44
myphoto2.png | 44
myavatar.png | 99
In reality, only one row should be returned for each user and the last avatar downloaded should be used as primary avatar. How can I fix this?
You can try below -
SELECT * FROM user_score us LEFT JOIN user_avatar ua ON us.gamerid=ua.userid
and ua.last_update =
(select max(last_update) from user_avatar ua1 where ua.userid=ua1.userid)
You should select the value for the most recent avatar .. probably related ton the max id for user_id
SELECT ua.*, us.*
FROM user_score us
INNER JOIN (
select user_id, max(id) max_id
from user_avatar
) t on t.user_id = us.user_id
LEFT JOIN user_avatar ua
ON us.gamerid=ua.u serid
AND t.max_id = ua.id
In reality, the last avatar downloa

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 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 |
+----+---------+--------+------------+

Simplify a complicated count SQL statement

I would like to know if there is a way to simplify the following SQL statement. This is my table.
| SID | name | l1 | l2 | sch |
| 1 | john | | | sch |
| 2 | mary | l1 | | |
| 3 | zack | l1 | l2 | |
| 4 | paul | l1 | l2 | sch |
Either l1 or l2 is filled, or both can be filled
Not every 'sch' has a value
What I do is to calculate a daily summary table, but I do it via PHP, I am wondering if it can be done just using SQL. So eg,
- Total count (This is just count(name))
- Count(sch)
- If !empty (l1) OR !empty (l2) THEN l + 1
So now, based on the above
Total count = 4
count(sch) = 2
count(l1 || l2) = 3
Can it be done in SQL?
You didn't say if l1, l2 and sch columns could contain NULL or not.
If these columns can not contain NULL, the mysql query would be like:
SELECT COUNT(*) AS `count`, SUM(sch<>'') AS count_sch,
SUM(l1<>'' OR l2<>'') AS count_l
FROM your_table
If these columns can contain NULL, the mysql query would be like:
SELECT COUNT(*) AS `count`, SUM(sch IS NOT NULL) AS count_sch,
SUM(l1 IS NOT NULL OR l2 IS NOT NULL) AS count_l
FROM your_table
select count(name) name_count,
count(coalesce(nullif(l1,''), nullif(l2,''))) l1_or_l2_count,
count(sch) sch_count
from your_table;
Yes:
select count(*) as count, count(sch) as school,
sum(case when l1 is not null or l2 is not null then 1 else 0 end)
from table

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'

Categories