Help with complex mysql query - mysql

I have an existing mysql query that I need to add to and I'm not sure how to go about it.
Here is my current sql query.
SELECT tbl_brokerage_names.brokerage_id, tbl_brokerage_names.short_name,
b.indication, b.max_indication
FROM tbl_brokerage_names
LEFT JOIN (
SELECT * FROM tbl_recommendation_brokerages
WHERE recommendation_id = {$_GET['id']}
) b ON (tbl_brokerage_names.brokerage_id = b.brokerage_id)
ORDER BY tbl_brokerage_names.short_name ASC
Here is the query that I need to work into the previous query.
SELECT * , COUNT( * )
FROM tbl_streetaccounts
JOIN tbl_brokerage_names
WHERE tbl_brokerage_names.brokerage_id = tbl_streetaccounts.brokerage_id
Basically I need to return a count, so I need to combine these two queries.

You should run these as two separate queries.
The COUNT(*) query will return a single row, so there's no way to "combine" it with the first query while preserving the multi-row result of the first query.
Also, when you SELECT *, COUNT(*) you will get columns from some arbitrary row.
By the way, you have a glaring SQL injection vulnerability. Don't interpolate $_GET parameters directly in your SQL query. Instead, coerce it to an integer:
<?php
$id = (int) $_GET['id'];
$sql = "SELECT ... WHERE recommendation_id = {$id}";

Like #Bill said, you cannot get the count in every row without really weird syntax, but you can get an overall count using GROUP BY ... WITH ROLLUP.
e.g.:
<?php
$id = mysql_real_escape_string($_GET['id']); //works with anything, not just numbers
$query = "
SELECT tbl_brokerage_names.brokerage_id
, tbl_brokerage_names.short_name
, b.indication
, b.max_indication
, count(*) as rowcount
FROM tbl_brokerage_names
LEFT JOIN (
SELECT * FROM tbl_recommendation_brokerages
WHERE recommendation_id = '$id' //The single quotes are essential for safety!
) b ON (tbl_brokerage_names.brokerage_id = b.brokerage_id)
GROUP BY tbl_brokerage_names.brokerage_id WITH ROLLUP
ORDER BY tbl_brokerage_names.short_name ASC
";
The GROUP BY .. WITH ROLLUP will add an extra line to the result with all NULL's for the non aggregated columns and a grand total count.
If you have any lines where rowcount > 0 then you need to add extra clauses from table b to the group by clause to prevent MySQL from hiding arbitrary rows.
Table tbl_brokerage_names is already fully defined because you are grouping by the primary key.

Related

sub query returns more than 1 - issue with passing values into subquery

I am running the following query which keep stating that more then one row is given:
select filestorage.id
from `filestorage`
where (SELECT LEFT(filestorage_inst_data.value, length(filestorage_inst_data.value - 1)) as seconds
FROM filestorage_inst_data
WHERE filestorage_inst_data.parameter = "Time" AND filestorage_inst_data.filestorage_id = filestorage.id) <= 3600
For some reason, the only very first value is passed into the subquery. Also, if I do set a limit within the subquery than the data is fetched fine, it's just I don't see why query would fetch multiple results?
Try this:
SELECT filestorage.id
FROM filestorage f
WHERE EXISTS(SELECT 1 FROM filestorage_inst_data fid
WHERE fid.parameter = 'Time'
AND fid.filestorage_id = f.id
AND CAST(LEFT(fid.value, length(fid.value - 1)) AS UNSIGNED) <= 3600)
You have to pass a specific one row when giving a select statement on where clause. select clause that, the one you using on where clause must return one unique row. for example.
"SELECT * FROM user WHERE role_id=(SELECT role_id FROM user_role WHERE role_name='Admin');"

merging SQL statements and how can it affect processing time

Let's assume I have the following tables:
items table
item_id|view_count
item_views table
view_id|item_id|ip_address|last_view
What I would like to do is:
If last view of item with given item_id by given ip_address was 1+ hour ago I would like to increment view_count of item in items table. And as a result get the view count of item. How I will do it normally:
q = SELECT count(*) FROM item_views WHERE item_id='item_id' AND ip_address='some_ip' AND last_view < current_time-60*60
if(q==1) then q = UPDATE items SET view_count = view_count+1 WHERE item_id='item_id'
//and finally get view_count of item
q = SELECT view_count FROM items WHERE item_id='item_id'
Here I used 3 SQL queries. How can I merge it into one SQL query? And how can it affect the processing time? Will it be faster or slower than previous method?
I don't think your logic is correct for what you describe that you want. The query:
SELECT count(*)
FROM item_views
WHERE item_id='item_id' AND
ip_address='some_ip' AND
last_view < current_time-60*60
is counting the number of views longer ago than your time frame. I think you want:
last_view > current_time-60*60
and then have if q = 0 on the next line.
MySQL is pretty good with the performance of not exists, so the following should work well:
update items
set view_count = view_count+1
WHERE item_id='item_id' and
not exists (select 1
from item_views
where item_id='item_id' AND
ip_address='some_ip' AND
last_view > current_time-60*60
)
It will work much better with an index on item_views(item_id, ip_address, last_view) and an index on item(item_id).
In MySQL scripting, you could then write:
. . .
set view_count = (#q := view_count+1)
. . .
This would also give you the variable you are looking for.
update target
set target.view_count = target.view_count + 1
from items target
inner join (
select item_id
from item_views
where item_id = 'item_id'
and ip_address = 'some_ip'
and last_view < current_time - 60*60
) ref
on ref.item_id = target.item_id;
You can only combine the update statement with the condition using a join as in the above example; but you'll still need a separate select statement.
It may be slower on very large set and/or unindexed table.

ORDER BY Causes MySQL query to become Extremely Slow

I have the following query:
SELECT *
FROM products
INNER JOIN product_meta
ON products.id = product_meta.product_id
JOIN sales_rights
ON product_meta.product_id = sales_rights.product_id
WHERE ( products.categories REGEXP '[[:<:]]5[[:>:]]' )
AND ( active = '1' )
AND ( products.show_browse = 1 )
AND ( product_meta.software_platform_mac IS NOT NULL )
AND ( sales_rights.country_id = '240'
OR sales_rights.country_id = '223' )
GROUP BY products.id
ORDER BY products.avg_rating DESC
LIMIT 0, 18;
Running the query with the omission of the ORDER BY section and the query runs in ~90ms, with the ORDER BY section and the query takes ~8s.
I've browsed around SO and have found the reason for this could be that the sort is being executed before all the data is returned, and instead we should be running ORDER BY on the result set instead? (See this post: Slow query when using ORDER BY)
But I can't quite figure out the definitive way on how I do this?
I've browsed around SO and have found the reason for this could be
that the sort is being executed before all the data is returned, and
instead we should be running ORDER BY on the result set instead?
I find that hard to believe, but if that's indeed the issue, I think you'll need to do something like this. (Note where I put the parens.)
select * from
(
SELECT products.id, products.avg_rating
FROM products
INNER JOIN product_meta
ON products.id = product_meta.product_id
JOIN sales_rights
ON product_meta.product_id = sales_rights.product_id
WHERE ( products.categories REGEXP '[[:<:]]5[[:>:]]' )
AND ( active = '1' )
AND ( products.show_browse = 1 )
AND ( product_meta.software_platform_mac IS NOT NULL )
AND ( sales_rights.country_id = '240'
OR sales_rights.country_id = '223' )
GROUP BY products.id
) as X
ORDER BY avg_rating DESC
LIMIT 0, 18;
Also, edit your question and include a link to that advice. I think many of us would benefit from reading it.
Additional, possibly unrelated issues
Every column used in a WHERE clause should probably be indexed somehow. Multi-column indexes might perform better for this particular query.
The column products.categories seems to be storing multiple values that you filter with regular expressions. Storing multiple values in a single column is usually a bad idea.
MySQL's GROUP BY is indeterminate. A standard SQL statement using a GROUP BY might return fewer rows, and it might return them faster.
If you can, you may want to index your ID columns so that the query will run quicker. This is a DBA-level solution, rather than a SQL solution - tuning the database will help overall performance.
The issue in the instance of this query, was that by using GROUP BY and ORDER BY in a query, MySQL is unable to use the index if the GROUP BY and ORDER BY expressions are different.
Related Reading:
http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html
http://mysqldba.blogspot.co.uk/2008/06/how-to-pick-indexes-for-order-by-and.html

codeigniter pagination after union

I have a script that makes a union with different tables of my database.
I need to show the result with pagination to make it more readable to the user.
This is my code:
//Fetch
foreach($query_info->result() as $info){
//I'm going to create a query for each row i get from the db
$query [] = (' (
SELECT
'.$info->slug.'_dog.`name`,
`age`,
'.$info->slug.'_breed.`name` as breed,
`sex`,
`link`,
`sterilized`,
\''.$info->slug.'\' as prefix
FROM
'.$info->slug.'_dog,
'.$info->slug.'_breed
WHERE
'.$info->slug.'_dog.breed = '.$info->slug.'_breed.id
AND
'.$info->slug.'_dog.type = '.$type.'
)';
}
//implode different UNION
$query_final = implode(" UNION ",$query);
As you can see the the query could be very big.
How can I paginate?
My second option is make a full View with all the result of the tables, about 10-20 tables...
Which is faster for the database? the "big" UNION or a View?
I don't know which is faster, but to do the UNION, you can add an offset and limit to the end of the total query for your pagination. In only the first query, you can add SQL_CALC_FOUND_ROWS in the select. Something like this: (Note that there is no comma after SQL_CALC_FOUND_ROWS)
(SELECT SQL_CALC_FOUND_ROWS column1, column2 FROM etc...) UNION (SELECT column1, column2 FROM etc...) LIMIT 0,10
After you have ran the query, immediately run this query:
SELECT FOUND_ROWS();
It will return the total records like there was no LIMIT on your query. So basically you do not have to run the query again to count the results.
For creating the rest of the pagination, you can use CI it's own pagination library:
http://codeigniter.com/user_guide/libraries/pagination.html
// Edit:
For returning data from the model, you could use something like this:
$results['data'] = $this->db->query($sql)->result_array();
$total = $this->db->query('SELECT FOUND_ROWS() as total')->row_array();
$results['total'] = $total['total'];
return $results;
It can probably be optimized a bit more.

How to get random rows in inner join using mysql?

I am using following queries to fetch random rows,
'SELECT * FROM TABLENAME1 ORDER BY RAND() LIMIT 1' . this query will return random rows.But i need to get the name which is placed in another table.so here i have to use join queries.
'SELECT tablename1.*,tablename2.name FROM tablename1 INNER JOIN tablename2 ON tablename1.id = tablename2.id ORDER BY RAND(tablename1.id)'
The above queries return same rows everytime, I am not getting random rows.Kindly help me on this.
RAND(tablename1.id) uses tablename1.id as the seed for the pseudorandom number generator, so it will give you the same result every time. Try just using RAND() in your second query.
For some reason, you're seeding the 2nd version of the query with tablename1.id.
Calls to RAND() with the same seed value return the same results.