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>
Related
unfortunately i have to do this in mysql / php . I looked for three days, and there is like 10.000 explantions of this but NONE (and I repeat NONE) works for me. I tried it all. I have to ask, sorry.
I have two tables - articles and control.
table "articles"
------------------
art_id | name |
------------------
1 | aaa |
2 | bbb |
3 | ccc |
4 | ddd |
table "control"
--------------------------------------------
con_id | art_id | data |
--------------------------------------------
1 | 1 | something-a |
2 | 2 | something-b |
3 | 1 | something-a |
4 | 2 | something-c |
5 | 3 | something-f |
art_id exists in both tables. Now what i wanted - for query:
"select * from articles order by art_id ASC" displayed in a table
to have also one cell displaying the count for each of art_id's from table CONTROL...
and so i tried join, left join, inner join - i get errors ... I also tried for each get only one result (for example 2 for everything)... this is semi-right but it displays the array of correct results and it's not even with join!!! :
$query = "SELECT art_id, count(*) as counting
FROM control GROUP BY art_id ORDER BY con_id ASC";
$result = mysql_query($query);
while($row=mysql_fetch_array($result)) {
echo $row['counting'];
}
this displays 221 -
-------------------------------------------------
art_id | name | count (this one from control) |
-------------------------------------------------
1 | aaa | 221 |
2 | bbb | 221 |
3 | ccc | 221 |
and it should be:
for art_id(value1)=2,
for art_id(2)=2,
for art_id(3)=1
it should be simple - like a count of values from CONTROL table displayed in query regarding the "articles" table...
The result query on page for table articles should be:
"select * from articles order by art_id ASC"
-------------------------------------------------
art_id | name | count (this one from control) |
-------------------------------------------------
1 | aaa | 2 |
2 | bbb | 2 |
3 | ccc | 1 |
So maybe i should go with JOIN or with join plus for each... Tried tha too, but then i'm not sure what is the proper thing to echo... all-in-all i'm completely lost here. Please help. Thank you.
So imagine this in two steps:
Get the counts per art_id from the control table
Using your articles table, pick up the counts from step 1
That will give you a query that looks like this:
SELECT a.art_id, a.name, b.control_count
FROM articles a
INNER JOIN
(
SELECT art_id, COUNT(*) AS control_count
FROM control
GROUP BY art_id
) b
ON a.art_id = b.art_id;
Which will give you the results you're looking for.
However, instead of using a subquery, you can do it all in one shot:
SELECT a.art_id, a.name, COUNT(b.art_id) AS control_count
FROM articles a
INNER JOIN control b
ON a.art_id = b.art_id
GROUP BY a.art_id, a.name;
SQL Fiddle demo
SELECT *, (SELECT COUNT(control.con_id) FROM control WHERE control.art_id = articles.art_id) AS count_from_con FROM articles ORDER BY art_id DESC;
If I understood your question right, this query should do the trick.
Edit: Created the tables you have described, and it works.
SELECT * FROM articles;
+--------+------+
| art_id | name |
+--------+------+
| 1 | aaa |
| 2 | bbb |
| 3 | ccc |
| 4 | ddd |
+--------+------+
4 rows in set (0.00 sec)
SELECT * FROM control;
+--------+--------+------+
| con_id | art_id | data |
+--------+--------+------+
| 1 | 1 | NULL |
| 2 | 2 | NULL |
| 3 | 1 | NULL |
| 4 | 2 | NULL |
| 5 | 3 | NULL |
+--------+--------+------+
5 rows in set (0.00 sec)
SELECT *, (SELECT COUNT(control.con_id) FROM control WHERE control.art_id = articles.art_id) AS count_from_con FROM articles ORDER BY art_id ASC;
+--------+------+----------------+
| art_id | name | count_from_con |
+--------+------+----------------+
| 1 | aaa | 2 |
| 2 | bbb | 2 |
| 3 | ccc | 1 |
| 4 | ddd | 0 |
+--------+------+----------------+
You haven't quite explained what you want to accomplish with the print out but here is an example in PHP: (Use PDO instead of mysql_)
$pdo = new PDO(); // Make your connection here
$stm = $pdo->query('SELECT *, (SELECT COUNT(control.con_id) FROM control WHERE control.art_id = articles.art_id) AS count_from_con FROM articles ORDER BY art_id ASC');
while( $row = $stm->fetch(PDO::FETCH_ASSOC) )
{
echo "Article with id: ".$row['art_id']. " has " .$row['count_from_con'].' connected rows in control.';
}
Alternatively with the mysql_ extension:
$result = mysql_query('SELECT *, (SELECT COUNT(control.con_id) FROM control WHERE control.art_id = articles.art_id) AS count_from_con FROM articles ORDER BY art_id ASC');
while( $row = mysql_fetch_assoc($result) )
{
echo "Article with id: ".$row['art_id']. " has " .$row['count_from_con'].' connected rows in control.';
}
This should be enough examples to help you accomplish what you need.
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 |
+------+------+-------+
How to delete all rows from a mysql table if values of two columns are equal
Example Table
invoice_id| item_id | name | invoiced_qty | received_qty
---------------------------------------------------------
| 1 | 1 | item1 | 3 | 2
| 2 | 2 | item2 | 5 | 5
| 3 | 1 | item3 | 4 | 3
| 4 | 2 | item4 | 2 | 2
| 5 | 1 | item5 | 5 | 5
After deleting table needs to retains
invoice_id| item_id | name | invoiced_qty | received_qty
---------------------------------------------------------
| 1 | 1 | item1 | 3 | 2
| 3 | 1 | item3 | 4 | 3
The select query which i created is
SELECT * FROM table1 A
INNER JOIN table1 B ON A.item_id = B.item_id
AND A.invoice_id = B.invoice_id
AND A.invoiced_qty = B.received_qty
Thanks
Why not just SQL Fiddle:
DELETE FROM table1
WHERE invoiced_qty = received_qty
Your edit does not change anything. He is the SQL Fiddle demonstrating your SELECT query. According to your sample data A.invoice_id will never equal B.invoice_id. So you will not get any results.
Try this :
DELETE FROM table1 A
INNER JOIN table1 B ON A.item_id = B.item_id
WHERE A.invoiced_qty = B.received_qty
You could simply wrap your select statement and select values to be deleted by id, like this:
DELETE FROM table1
WHERE item_id IN (SELECT item_id FROM table1 A
INNER JOIN table1 B ON A.item_id = B.item_id
AND A.invoice_id = B.invoice_id
AND A.invoiced_qty = B.received_qty)
however you should accept answer by Linger as it is more straightforward solution, mine was to indicate that if you have something selected usually you can wrap and delete.
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.
here is my 2 tables, id for a inner join event. i want to do this thing:
in table b, there has 10 albums, i want random get out 4 albums. then each album select one record, the record is random position in the album.
so that i will get 4 records back (these 4 records with no duplicate id), then take these 4 records for a inner join query, to get the title from table a.
here just little records just for test. in the fact, i have 300,000 records in table a and 2,000,000 records in table b.
table a
+-----+-------+
| id | title |
+-----+-------+
| 1 | a1 |
+-----+-------+
| 2 | a2 |
+-----+-------+
| 3 | a3 |
+-----+-------+
| 4 | a4 |
+-----+-------+
| 5 | a5 |
+-----+-------+
| 6 | a6 |
+-----+-------+
table b
+-----+--------+
| id | album |
+-----+--------+
| 1 | album1 |
+-----+--------+
| 2 | album1 |
+-----+--------+
| 3 | album1 |
+-----+--------+
| 6 | album1 |
+-----+--------+
| 2 | album2 |
+-----+--------+
| 3 | album2 |
+-----+--------+
| 5 | album3 |
+-----+--------+
| 6 | album3 |
+-----+--------+
| 3 | album4 |
+-----+--------+
| 2 | album5 |
+-----+--------+
| 4 | album5 |
+-----+--------+
| 5 | album5 |
+-----+--------+
| 1 | album6 |
+-----+--------+
| 3 | album6 |
+-----+--------+
| 2 | album7 |
+-----+--------+
| 4 | album7 |
+-----+--------+
| 1 | album8 |
+-----+--------+
| 5 | album8 |
+-----+--------+
| 3 | album9 |
+-----+--------+
| 2 | album10|
+-----+--------+
| 5 | album10|
+-----+--------+
I am not good at mysql query. In my mind I would do
select * from b group by album order by random() limit 0,4
get back 4 album, then do a inner join query (this query not correct, how to check the b.id no duplicate?)
select * from b inner join a on b.id = a.id where (select id from b where b.album = '".$row['album']."' order by random() limit 1)
I need an easy and quicker method, the best is just use one query. many thanks.
AFAIR, "ORDER BY RAND()" is extremely slow solutions, especially on tables like you have (2 million+ records), so I'd recommend looking at something similar to these kind of articles first: http://www.greggdev.com/web/articles.php?id=6
So, you should know the number of records in your table before running the query and then do something like:
"SELECT * FROM `album` LIMIT 1 OFFSET " . rand(0,$count)
This will return you 1 random row a bit more efficiently, I believe.
Also, I think it's not a good idea to store album references as string in tracks table, you'd rather use a proper integer foreign key album_id referenced to albums.id. Then you can join both tables much fatser. If I were you, I'd do first:
ALTER TABLE `tracks` add column `album_id` int;
UPDATE `tracks` SET `album_id` = SUBSTRING(`album`,5);
Then, after doing this and combining with the solution above, launch something like:
"SELECT * FROM `album` INNER JOIN `tracks`ON `tracks`.`album_id` = `albums`.`id` LIMIT 1 OFFSET " . rand(0,$count)
Since I'm neither an expert on MySQL nor on PHP, I'll try with pseudocode and generic SQL. I have renamed your tables to albums and tracks for sake of readability.
First fetch the four random records to your PHP application:
select id from albums order by random() limit 4
Second, iterate over the resulting result set of four IDs and fetch the corresponding tracks (pseudo-php):
foreach($album_ids as $id):
execute_query("select id from tracks where album_id = ? order by random(), limit 1", $id)
It is not obvious to me how you match your tracks to their albums. You should have something like tracks.album_id as a foreign key to albums.id, that's how I designed my queries. You should adapt as appropriate, the underlying logic behind my solution should remain the same.