MYSQL RANDOM SELECT UNIQUE ROWS - Excluding previously selected rows - php

I have a table of 16K entries
I want to extract random 44 entries
but I don't want to repeat the same entries more then once (ever)
so i have a per-user list that keeps the already used 'IDs' as a comma-separated string in a table.
and I use that list to SELECT ... NOT IN (used_IDs)
The issue is that this list is getting too big and the sql call fails because of size i believe
Any idea on how to do that more usefully?
Questions table:
+------+-------+-------+
| id | Qtext | Tags |
+------+-------+-------+
Test table:
+------+-------+
| id | QIDs |
+------+-------+
Results table:
+------+-------+-------+
| id | tID | uID |
+------+-------+-------+
I need to select unique random values from Questions table based on the results table. (which associates test ID with Question IDs)
Currently trying to use:
SELECT DISTINCT `questions`.`ID`
FROM `questions`, `tests`, `results`
WHERE
`questions`.`ID` NOT IN (`tests`.`qIDs`)
AND `results`.`uID` = 1 AND `tests`.`ID` = `results`.`tID`
AND 4 IN ( `questions`.`tags`)
AND "http://www.usmlestep2qna.com" = `provider`
ORDER BY RAND() LIMIT 27;
Any ideas?

Instead of placing the used user Id values in a comma-separated string in one column, you could create a tall table to store them. This should yield better preformance

Rather than using a single row with a (potentially huge) CSV, why not use a nicely indexed table and an outer join to pick unmatched records. I have an example from my test database:
mysql> select * from first;
+------+-------+
| id | title |
+------+-------+
| 1 | aaaa |
| 2 | bbbb |
| 3 | cccc |
| 4 | NULL |
| 6 | gggg |
+------+-------+
5 rows in set (0.00 sec)
mysql> select * from second;
+------+----------+------+------+-------+------+
| id | first_id | one | two | three | four |
+------+----------+------+------+-------+------+
| 1 | 1 | 3 | 0 | 4 | 6 |
| 1 | 2 | 4 | 4 | 1 | 2 |
| 3 | 3 | 1 | NULL | 3 | 4 |
+------+----------+------+------+-------+------+
3 rows in set (0.00 sec)
mysql> select a.id from first a join second b on a.id=b.first_id;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
mysql> select a.id from first a
left outer join second b on a.id=b.first_id where b.first_id is null;
+------+
| id |
+------+
| 4 |
| 6 |
+------+
2 rows in set (0.00 sec)
This should improve your performance rather nicely.

Related

SQL query to return combined data of all rows that don't belong to current user

Imagine this is my table:
----------------------------------------------------
| id | user_id | amount_1 | amount_2 | amount_3 |
----------------------------------------------------
| 1 | 1 | 2 | 3 | 4 |
----------------------------------------------------
| 2 | 2 | 2 | 1 | 1 |
----------------------------------------------------
| 3 | 2 | 1 | 2 | 2 |
----------------------------------------------------
| 4 | 3 | 2 | 1 | 4 |
----------------------------------------------------
I need a query that gives me one result set for every entry that belongs to my current user, and then returns everything else as a single combined row with the amounts summed.
So in this case if I am user 1, I should get the following rows back:
---------------------------------------
| id | amount_1 | amount_2 | amount_3 |
---------------------------------------
| 1 | 2 | 3 | 4 | my own amounts
---------------------------------------
| 2 | 5 | 4 | 7 | everyone else's amounts
---------------------------------------
Any tips?
I've considered it might be a better idea to just filter the data in the code (php). Please help i'm starting to hate myself
You could use a UNION in sql
select 1 id, amount_1, amount_2, amount_3
from my_table
where user_id = 1
union
select 2 , sum(amount_1) , sum(amount_2), sum(amount_3 )
from my_table
where user_id <> 1
You can do with one query using union:
SELECT user_id, amount_1, amount_2, amount_3
FROM table
WHERE user_id = YOUR_USER_ID
UNION
SELECT -1, SUM(amount_1) AS amount_1, SUM(amount_2) AS amount_2, SUM(amount_3) AS amount_3
FROM table
WHERE user_id != YOUR_USER_ID
You can use aggregation in one fell swoop:
select (case when user_id = 1 then id end) as my_user_or_not,
sum(amount_1), sum(amount_2), sum(amount_3)
from t
group by my_user_or_not;
The null values in the first column indicate another user. You have labelled the column id, which is a bit problematic if you were -- for instance -- to choose user_id = 2 in your example. NULL seems safer for this purpose.

SQL query column with cookie data

My cookie member names are the first and last name of the user with a space in between. I'm trying to get the user ID from the row with the following query:
SELECT * FROM dbo.users WHERE firstname IN('{$_COOKIE['member_name']}') AND lastname IN('{$_COOKIE['member_name']}')
I'm not getting any results from this, and I can't figure out what I'm doing wrong. The LIKE operator isn't working either.
why using IN() ?
i have try like this query, can work for my data
maybe you can check table column data type and output query SQL.
(root#localhost) [test]> select * from a;
+------+------+-------+
| team | type | value |
+------+------+-------+
| A | 0 | 10 |
| A | 1 | 5 |
| B | 0 | 10 |
| B | 0 | 10 |
| A | 1 | 20 |
| B | 1 | 20 |
+------+------+-------+
6 rows in set (0.00 sec)
(root#localhost) [test]> select * from a where team in ('A') and type in ('0');
+------+------+-------+
| team | type | value |
+------+------+-------+
| A | 0 | 10 |
+------+------+-------+
1 row in set (0.00 sec)

Left Join with pagination (LIMIT)

Imagine I have a table like this with a many to one relationship
Table1
id | name
1 | as
2 | df
3 | gh
and
id | othercontents | table1relationship
1 | qw | 1
2 | er | 2
3 | ty | 3
4 | ui | 3
if I run a select query on Table1 with a left join for Table2 but limit it to 3 results I will get 3 rows returned
1 - as - qw
2 - df - er
3 - gh - ty
however I want
1 - as - qw
2 - df - er
3 - gh - [ty, ui]
Now, currently I am selecting it as normal and then putting othercontents into an array myself to turn my rows into how I want them, but the problem remains that I can not return all the rows I want.
Logically, I suppose I want to limit to X unique table1.id's rather than limiting to just X rows, but I do not know a way to implement this logic if it is even possible.
Ofcourse this is easy if I select everything in the database and then sort it in PHP but this is too intensive and I don't want to select 20,000 rows just to get ~10 rows. I suppose a hacky way around this would be to select 30 rows and then do my own sorting and return the 10 as I want them but it still seems silly to me to select more than I need.
Perhaps worth mentioning but I am using Symfony3 w/ Doctrine and using query builder. But I am not asking for copy/paste answer to my problem rather just a push in the direction so I can work on my implementation.
Thanks
Here is a query that will give you the result ( if i understand you correct).
SELECT t1.id,t1.name,
CONCAT( IF(sum(1)>1,'[',''), GROUP_CONCAT(t2.othercontents), IF(sum(1)>1,']','')) AS name2
FROM (
SELECT *
FROM table1
LIMIT 3
) as t1
LEFT JOIN table2 t2 on t2.table1relationship = t1.id
GROUP BY t2.table1relationship;
sample
mysql> SELECT * from table1;
+----+------+
| id | name |
+----+------+
| 1 | as |
| 2 | df |
| 3 | gh |
+----+------+
3 rows in set (0,00 sec)
mysql> SELECT * from table2;
+----+---------------+--------------------+
| id | othercontents | table1relationship |
+----+---------------+--------------------+
| 1 | qw | 1 |
| 2 | er | 2 |
| 3 | ty | 3 |
| 4 | ui | 3 |
+----+---------------+--------------------+
4 rows in set (0,00 sec)
result
mysql> SELECT t1.id,t1.name,
-> CONCAT( IF(sum(1)>1,'[',''), GROUP_CONCAT(t2.othercontents), IF(sum(1)>1,']','')) AS name2
-> FROM (
-> SELECT *
-> FROM table1
-> LIMIT 3
-> ) as t1
-> LEFT JOIN table2 t2 on t2.table1relationship = t1.id
-> GROUP BY t2.table1relationship;
+----+------+---------+
| id | name | name2 |
+----+------+---------+
| 1 | as | qw |
| 2 | df | er |
| 3 | gh | [ui,ty] |
+----+------+---------+
3 rows in set (0,00 sec)
mysql>

Cron job to identify multiple visits in the same url?

using a PHP cron job, how can I output the users who visited the same page over 2 times ?
Here is the mySQL table
id - user - page - timestamp
340 - 1 - page1 - 2009-05-18 22:11:11
339 - 1 - page1 - 2009-05-18 22:10:01
337 - 1 - page1 - 2009-05-18 22:06:00
336 - 2 - page3 - 2009-05-18 22:00:00
335 - 2 - page3 - 2009-05-18 21:56:00
This could be done easily with group by and count
select
user,page, count(*) as total
from table_name
group by user,page
having total > 2
UPDATE
how can i update another table with these results? user - page - times
This could be done using insert into select from in addition using on duplicate key update
Consider the following
mysql> select * from pages ;
+------+------+
| user | page |
+------+------+
| 1 | p1 |
| 1 | p1 |
| 1 | p1 |
| 2 | p1 |
| 2 | p1 |
| 2 | p2 |
| 3 | p2 |
| 3 | p2 |
| 3 | p2 |
+------+------+
create table page_view_log
(
user int,
page varchar(100),
times int,
primary key(user,page)
);
select * from page_view_log ;
Note that here the log table has composite key (user,page)
Now the following query will do the job
insert into page_view_log(user,page,times)
select
user,page,total from (
select user,page, count(*) as total
from pages
group by user,page
having total > 2
)x
on duplicate key update times = total
mysql> select * from page_view_log ;
+------+------+-------+
| user | page | times |
+------+------+-------+
| 1 | p1 | 3 |
| 3 | p2 | 3 |
+------+------+-------+
2 rows in set (0.00 sec)
Now lets add more page in the first table for user-1
insert into pages values (1,'p1'),(2,'p2'),(2,'p2');
Now if we run the above query again it will update the total for existing data and insert new values, the duplicate is considered using the composite key (user,page)
mysql> select * from page_view_log ;
+------+------+-------+
| user | page | times |
+------+------+-------+
| 1 | p1 | 4 |
| 2 | p2 | 3 |
| 3 | p2 | 3 |
+------+------+-------+

SQL: get data spread over 3 tables

I am trying to get some statistics for an online game I maintain. I am searching for an SQL statement to get the result on the bottom.
There are three tables:
A table with teams, each having a unique identifier.
table teams
---------------------
| teamid | teamname |
|--------|----------|
| 1 | team_a |
| 2 | team_x |
---------------------
A table with players, each having a unique identifier and optionally an affiliation to one team by it's unique teamid.
table players
--------------------------------
| playerid | teamid | username |
|----------|--------|----------|
| 1 | 1 | user_a |
| 2 | | user_b |
| 3 | 2 | user_c |
| 4 | 2 | user_d |
| 5 | 1 | user_e |
--------------------------------
Finally a table with events. The event (duration in seconds) is related to one of the players through their playerid.
table events.
-----------------------
| playerid | duration |
|----------|----------|
| 1 | 2 |
| 2 | 5 |
| 3 | 3 |
| 4 | 8 |
| 5 | 12 |
| 3 | 4 |
-----------------------
I am trying to get a result where the durations of all team members is summed up.
result
--------------------------
| teamid | SUM(duration) |
|--------|---------------|
| 1 | 14 | (2+12)
| 2 | 15 | (3+8+4)
--------------------------
I tried several combinations of UNION, WHERE IN, JOIN and GROUP but could not get it right. I am using PostgreSQL and PHP. Can anyone help me?
Just use sum with group by:
select t.teamid, sum(e.duration)
from team t
join players p on t.teamid = p.teamid
join events e on p.playerid = e.playerid
group by t.teamid
If you need all teams to be returned even if they don't have events, then use an outer join instead.
Try this
SELECT teamid, Sum(duration),
AS LineItemAmount, AccountDescription
FROM teams
JOIN teams ON teams.teamid = players.teamid
JOIN events ON players.playersid = events.playersid
JOIN GLAccounts ON InvoiceLineItems.AccountNo = GLAccounts.AccountNo
GROUP BY teamid
http://www.w3computing.com/sqlserver/inner-joins-join-two-tables/

Categories