MySQL select over the result set again - php

Edit (for clarification): Hannes wants to search records with a certain VID and associated records from the same table with the condition AID (from original records) = TID.
EDIT, because wrog problem description (sorry for that!)
I need a query in MySQL that loops through the selected rows and creates a result array. The pattern for this is shown below.
Currently I call a select again and again. Is there a way to create a single query from this? Thanks a lot!
Table structure
ID, VID, AID, ... (title, etc.) ... TID
Select over the vid field
SELECT id, vid, aid FROM tbl WHERE vid = 24;
ID VID AID
1 24 NULL
2 24 NULL
3 24 712
4 24 713
5 24 714
6 24 715
7 24 716
...
If aid not null => select the same table with the AID value as condition for another field
SELECT * FROM tbl WHERE TID = 112
SELECT * FROM tbl WHERE TID = 113
SELECT * FROM tbl WHERE TID = 114
SELECT * FROM tbl WHERE TID = 115
SELECT * FROM tbl WHERE TID = 116
Result should be (all null rows and the selected rows from above)
ID VID AID ...
1 24 NULL
2 24 NULL
75 24 712
79 24 713
88 24 714
92 24 715
97 24 716
The problem in this e.g. is that i need a loop and tones of selects. Pseudocode:
rows = []
foreach (main_select)
{
if (aid != null)
{
selected_row = select * from tbl where tid = aid
rows[] = selected_row
}
else
{
rows[] = main_select_row_from_iteration
}
}

You should be able to do all of that with a single query
SELECT tbl1.ID as TBL1_ID, tbl1.VID as TBL1_VID, tbl1.AID as TBL1_AID,
tbl2.ID as TBL2_ID, tbl2.VID as TBL2_VID, tbl2.AID as TBL2_AID
FROM tbl AS tbl1
LEFT JOIN tbl AS tbl2 ON tbl1.AID = tbl2.TID
WHERE tbl1.VID = 24 AND tbl1.AID IS NOT NULL
It's a self-join of the same table, the WHERE condition makes sure that you have the initial VID and that there's not NULL value in AID. The JOIN takes AID and matches it to TID.
The fields in the SELECT list are from both, separated via alias.

Related

How to update rows with new values for groups found and make this as fast as posible?

I have a table which have these fields: id, client_id, visit_id, number_of_visit.
This table can have from 30k to 100+k rows of data. The primary key is client_id and visit_id. (I cannot have a row with these two values being the same, but meanwhile client_id can appear more than 1 time having different visit_ids and so on).
The thing I want to do is to write values to the field number_of_visit, and this value should be a sequence number of this visit (the first, second, third and so on). I have the visit_id field which is actually a large number, and I need to order it as 'ASC' and write values to number_of_visit accordingly. I have this:
id client_id visit_id number_of_visit
1 103 10 0
2 103 11 0
3 104 12 0
4 105 13 0
5 105 15 0
6 105 16 0
And I want to do this:
id client_id visit_id number_of_visit
1 103 10 1
2 103 11 2
3 104 12 1
4 105 13 1
5 105 15 2
6 105 16 3
I write values to number_of_visit from 1 to n in the same order as visit_id values in ASC order.
This table is able to have more than 1 row with the same client_id, but cannot have a row with the same visit_id, because one visit_id is always relative to a particular client_id. I need to get all the visit_id's of all client_id's in this table, and update the number_of_visit field of these rows with numbers 1, 2, 3, which represent sequence numbers. (if this is the first visit, or it's the second, third and so on ...).
I have this PHP code:
$visit_ids = [];
$sql = "SELECT clientID, GROUP_CONCAT(id) as 'visit_ids' FROM table_report GROUP BY clientID";
$stmt = $pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch()) {
$visit_ids = explode(',', $row['visit_ids']);
$counter = 1;
foreach ($visit_ids as $id)
{
$query = "UPDATE table_report SET number_of_visit = :value WHERE id = :id";
$statement = $pdo->prepare($query);
$statement->execute([':value' => $counter, ":id" => $id]);
$counter++;
}
}
But the main problem is that it's too slow. The table contains about 40k rows now, but could have more than 100k+. When it's 40k it takes about an hour or two to complete.
What can I do to make this more faster? I have indexes on these fields.
Here's also EXPLAIN output:
Use row_number():
select t.*, row_number() over(partition by client_id order by visit_id) number_of_visit
from table_report t
If you wanted an update statement:
update table_report t
inner join (
select id, row_number() over(partition by client_id order by visit_id) number_of_visit
from table_report t1
) t1 on t1.id = t.id
set t.number_of_visit = t1.number_of_visit

Issue while copying tables on PHP SQL

All id are auto_increment. I will try explain my issue understandable. I just want copy 2 tables to another 2 tables. Here my tables at below:
table1
id number
10 100
11 102
12 105
13 106
table2
id number_id subnumber
52 10 10
53 11 15
54 13 40
You can see there is subnumber of some numbers. For example, WHERE id =11 from table1 has a subnumber on table2 and it is equal to 15. Now I should copy the table1 to the table : copy_table1
$sql1 = mysql_query('INSERT INTO
copy_table1 (copy_number)
SELECT
number
FROM
table1');
And the results of copy_table1 are:
copy_table1
id copy_number
100 100
101 102
102 105
103 106
Then I should copy table2 to another table copy_table2:
$sql2 = mysql_query('INSERT INTO
copy_table2 (copy_number_id, copy_subnumber)
SELECT
number_id, copy_subnumber
FROM
table2');
And the results of copy_table2 are:
id copy_number_id copy_subnumber
60 10 10
61 11 15
62 13 40
So, lets check the tables: copy_table1 and copy_table2.
You see, ID from copy_table1 is not equal and suitable to copy_number_id FROM copy_table2
And it brings me problem. How can I do that after copying tables id and copy_number_id will be suitable to each other?
$sql2=mysql_query('INSERT INTO
copy_table2 (copy_number_id, copy_subnumber)
SELECT DISTINCT
cp1.id, t2.subnumber
FROM
copy_table1 AS cp1
CROSS JOIN
table1 AS t1
USING (number)
INNER JOIN
table2 AS t2
ON
t1.id = t2.number_id ');

mysql, php, how to compare numbers

i have a database: database with a table: table and some fields:
id fname dphone count_pic dup_id
6055903 Karla 5126xxx798 1 57
6173767 Aaliyah 4082xxx534 4 39
5611411 Aaliyah 4082xxx534 15 39
5611211 Aaliyah 4082xxx534 18 39
4234798 Abby 3057xxx974 31 16
6166691 Walter 6178xxx280 1 74
3375576 Walter 6178xxx280 17 74
what i am trying to do is to select the fields that have the smallest count_pic and the ones that have the bigger count_pic and that have the same dup_id
any ideas how to do this in mysql?
thanks.
With this query you'll select the smallest and the biggest values of count_pic for every dup_id
SELECT MIN(count_pic) AS minpic,
MAX(count_pic) AS maxpic,
dup_id
FROM `table`
GROUP BY dup_id
If you also need corresponding rows, then you could use something like
SELECT *
FROM `table` t1
INNER JOIN (SELECT MIN(count_pic) AS minpic,
MAX(count_pic) AS maxpic,
dup_id
FROM `table`
GROUP BY dup_id) t2 ON t1.dup_id = t2.dup_id
AND (t1.count_pic = minpic
OR t1.count_pic = maxpic)

How to limit / offset query that returns several rows per unique ID?

My issue is that I need to paginate data from this query:
function search($search_term, $limit, $offset)
{
$id = $this->auth->get_user_id();
$query = $this->db->query("
SELECT user_id,
first_name,
cars_name,
cars_id
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
ORDER BY first_name ASC
");
$search_data = array();
foreach ($query->result() as $row) {
$search_data[$row->user_id]['name'] = $row->first_name;
$search_data[$row->user_id]['cars'][$row->cars_id] = array(
'cars_name' => $row->cars_name);
}
return $search_data;
}
A sample data table / query response would be:
1 JOE HONDA 123
1 JOE TOYOTA 124
2 MAC VW 125
2 MAC HONDA 126
2 MAC TESLA 127
3 STU SUBARU 128
3 STU KIA 129
-----------
Page 1
-----------
1 JOE HONDA 123
TOYOTA 124
2 MAC VW 125
HONDA 126
------------
Page 2
------------
3 STU SUBARU 128
KIA 129
If I enter a limit and offset at the end of MySQL query
...
LIMIT $limit
OFFSET $offset;
");
the limit and offset are applied to the total number of rows, not the the number of rows grouped by user.
I've tried using GROUP BY but was unable to make it work.
My goal is to make the query as above but LIMIT and OFFSET the query by a number of rows that counts users, not all rows.
Any ideas?
I don't see a way to do this in one query. My solution would be to get the count of unique ID's using a group by query with the same parameters:
SELECT COUNT(1) AS uid_count
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
GROUP BY user_profiles.user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
Then fetch the uid_countmysql_num_rows and use that to calculate pagination variables for the above query.
The solution really is to use a GROUP BY clause:
SELECT SQL_CALC_FOUND_ROWS
user_id,
first_name,
cars_name,
cars_id
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
GROUP BY user_id
ORDER BY first_name ASC
LIMIT 100
The order is important. GROUP BY first, then ORDER BY, and then OFFSET/LIMIT.
Notice the SQL_CALC_FOUND_ROWS up there? After the query has executed, if you want to get the total row count (including those who aren't returned because of the LIMIT clause), just use:
SELECT FOUND_ROWS() AS `count`
And fetch the count column.
However, like you said, the rows will collapse and you will lose some cars_name and cars_id values.
Another solution is to use GROUP_CONCAT, then split it in PHP:
SELECT
user_id,
first_name,
GROUP_CONCAT(cars_name SEPARATOR ','),
GROUP_CONCAT(cars_id SEPARATOR ','),
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
ORDER BY first_name ASC
LIMIT 100
This would give you something like:
1 JOE HONDA,TOYOTA 123,124
2 MAC VW,HONDA,TESLA 125,126,127
3 STU SUBARU,KIA 128,129
If you want to get a list like this
Page 1
----------------------
1 JOE HONDA 123
1 JOE TOYOTA 124
Page 2
----------------------
2 MAC VW 125
2 MAC HONDA 126
2 MAC TESLA 127
Page 3
----------------------
3 STU SUBARU 128
3 STU KIA 129
Forget about limit, do this instead:
A - First retrieve a list of user id's and insert that into a temp table
CREATE TEMPORARY TABLE `test`.`temp_user_ids` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INTEGER UNSIGNED NOT NULL,
PRIMARY KEY (`id`)
)
ENGINE = MEMORY
B - Next insert the relavant user_id's into the table.
INSERT INTO temp_user_ids
SELECT null, user_id
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
ORDER BY user_id DESC /*insert in reverse order !*/
The lowest user_id is the last_insert_id in the temptable, and the temp_table
items are in sequential order.
C - Set the SQL #var #current_id to the last_insert_id in the temp_table.
SELECT #current_id:= LAST_INSERT_ID()
D - Next select relevant rows from the table, using only the user_id you want.
SELECT count(*) as row_count,
up.user_id,
first_name,
group_concat(cars_name) as car_names,
group_concat(cars_id) as car_ids,
FROM user_profiles up
LEFT JOIN cars
ON cars.id_fk = up.user_id
INNER JOIN temp_user_ids t
ON (t.user_id = up.user_id)
WHERE t.id = #current_id
GROUP BY up.user_id
ORDER BY cars.id
E - Now lower the #current_id
SELECT #current_id:= #current_id - 1;
F - And repeat step D and E until there's no more rows to be had.
The first field row_count tells you the number of rows aggregated in the fields
car_names and car_ids. You can separate these fields by using php's explode.

summing two columns total in mysql

I want a resultset for this table:
ID Number_of_posts Number_of_user
1 100 21
2 23 34
as
ID Number_of_posts Number_of_user Number_of_posts_AND_Number_of_user
1 100 21 178
2 23 34 178
-----------------------------------------------
123 55
Is it possible to get the sum of two colums as another column/ as output in mysql?
To get cross-tab totals (horizontal and vertical):
select id,
number_of_posts as p,
number_of_users as u,
number_of_posts+number_of_users as p_and_u
from tbl
union all
select 99999 as id,
sum(number_of_posts) as p,
sum(number_of_users) as u,
sum(number_of_posts+number_of_users) as p_and_u
from tbl
order by 1
This will give you:
id p u p_and_u
----- --- --- -------
1 100 21 121
2 23 34 57
99999 123 55 178
You're complicating your query needlessly and using more memory that you have to. Pull the records in one query, then make another query to get the aggregates.
I know it doesn't answer your question, but it's what you should be doing instead. =)
SELECT id, number_of_posts, number_of_user,
(
SELECT SUM(number_of_posts + number_of_user)
FROM mytable
)
FROM mytable
SELECT SUM(Number_of_posts), SUM(Number_of_user) FROM table;
SELECT *,
(SELECT SUM(Number_of_posts) + SUM(Number_of_user) FROM TABLE) AS total
FROM table;
(Edit: Didn't originally notice it was the total total in the last column.)
Does MySQL support ROLLUP?
SELECT id,
SUM(Number_of_posts) Number_of_posts,
SUM(Number_of_user) Number_of_user,
SUM(Number_of_posts) + SUM(Number_of_user) Number_of_posts_AND_Number_of_user
FROM table
GROUP BY ROLLUP(id)
Edit: based on a quick search, in MySQL the last line might be GROUP BY id WITH ROLLUP.

Categories