I have a sql which is running perfectly okie. I want to convert it into active record query but don't know how to convert these sub query into active record
below is my sql:
Select A.*, B.*
FROM
( SELECT ( 6371 * acos( cos( radians(24.8964089) ) * cos( radians(RB.`lat`) )
* cos( radians(RB.`lng`) - radians(67.06749)) + sin(radians(24.8964089))
* sin( radians(RB.`lat`)))) AS distance, RB.`id` as BRID , R.`name` as NAME
FROM
`rent_br` RB
JOIN `rent` R ON R.`id` = RB.`restId`
JOIN `rent_category` RC on R.`id` = RC.`restId`
WHERE
TRUNCATE(RB.`lat`, 0) >= TRUNCATE(24.8964089,0)
AND TRUNCATE(RB.`lat`, 0) < TRUNCATE(25.8964089,0)
AND RB.`isdelivery` = '1'
AND RC.`catId` = 11
/*AND ( rent.`tags` like '%burger%' OR rent.`name` like '%burger%' OR info.`name` like '%burger%' )*/
HAVING
`distance` <= 50
ORDER BY
`distance` ASC ) A
JOIN
(select max(radius) as maxRadius, rent_br_delivery_radius.`rest_brId` as bbrId from `rent_br_delivery_radius`
group by bbrId) B on `A`.BRID = B.bbrId
WHERE A.distance <= B.maxRadius
ORDER BY A.NAME
the only thing you have to consider here is the use of the get_compiled_select function of the query builder - once you understand that - its pretty easy. You just have to split your queries and merge them together to one query
the following should do the job
your subselect
$strSubQuery = $this->db
->select('
( 6371 * acos( cos( radians(24.8964089) ) * cos( radians(RB.`lat`) )
* cos( radians(RB.`lng`) - radians(67.06749)) + sin(radians(24.8964089))
* sin( radians(RB.`lat`)))) AS distance, RB.`id` as BRID , R.`name` as NAME
')
->from('rent_br RB')
->join('rent r', 'r.id = RB.restId')
->join('rent_category RC', 'R.id = RC.restId')
->where('TRUNCATE(RB.`lat`, 0) >= TRUNCATE(24.8964089,0)', NULL, false)
->where('TRUNCATE(RB.`lat`, 0) < TRUNCATE(25.8964089,0)', NULL, false)
->where('RB.isdelivery`', 1)
->where('RC.catId', 11)
->having('distance <=', 50)
->order_by('distance', 'ASC')
->get_compiled_select();
your subjoin query
$strSubQueryJoin = $this->db
->select_max('radius', 'maxRadius')
->select('rent_br_delivery_radius.rest_brId AS bbrId')
->from('rent_br_delivery_radius')
->group_by('bbrId')
->get_compiled_select();
and finally we put this pieces together
$query = $this->db
->select('A.*, B.*')
->from($strSubQuery.' A', false)
->join('('.$strSubQueryJoin.') B', 'A.BRID = B.bbrId', 'INNER', false)
->where('A.distance <= B.maxRadius', NULL, false)
->order_by('A.NAME')
->get();
That should pretty much do the job.
Related
I'm creating a query that get's the total number of customers within a certain distance of each one of our stores using the Haversine Formula.
This formula is fairly big and looks ugly when I use it in a query, so I decided to create a function out of it so that my queries would look cleaner.
The issue is that when I run the query using the function, I get this error: Error Code: 2013. Lost connection to MySQL server during query.
As soon as I use the actual formula instead of the function in my query it runs as expected. Anyone know why this is happening? Is this a time out issue because the query is taking to long to run?
Here is my user created Haversine function:
CREATE DEFINER=`MYADMIN`#`%` FUNCTION `Haversine`(lat1 DECIMAL(15,10), lng1 DECIMAL(15,10), lat2 DECIMAL(15,10), lng2 DECIMAL(15,10)) RETURNS decimal(11,2)
BEGIN
RETURN (3959 * acos( cos( radians(lat1) ) * cos( radians( lat2 ) ) * cos( radians( lng2 ) - radians(lng1) ) + sin( radians(lat1) ) * sin(radians(lat2)) ));
END
This query causes the lost connection error:
SELECT s.StoreID
, count(c.PBID) as customers
, sum(c.Status = 'null') as 'Not Active'
, sum(c.Status = 'Left Message') as 'Left Message'
from console.Stores s
cross
join console.Customers c
where console.Haversine(s.Latitude,s.Longitude,c.Lat,c.Lng) <= 5
and c.DNC = '0'
and c.Status != 'Do Not Call'
group
by s.StoreID;
This is the same query without the function which runs as expected:
SELECT s.StoreID, count(c.PBID) as customers, sum(c.Status = 'null') as 'Not Active', sum(c.Status = 'Left Message') as 'Left Message' from console.Stores s cross join console.Customers c where (3959 * acos( cos( radians(s.Latitude) ) * cos( radians( c.Lat ) ) * cos( radians( c.Lng ) - radians(s.Longitude) ) + sin( radians(s.Latitude) ) * sin(radians(c.Lat)) ) ) <= 5 and c.DNC = '0' and c.Status != 'Do Not Call' group by s.StoreID;
This is a bit of a strange one: I have the following MySQL Stored Proc (or function) and the majority can be ignored safely unless there is a need to understand the full picture. The problem is the ORDER BY clause.
BEGIN
SELECT DISTINCT e.*, ( 3959 * acos( cos( radians(in_latitude) ) * cos( radians( e.address_latitude ) )
* cos( radians(e.address_longitude) - radians(in_longitude)) + sin(radians(in_latitude))
* sin( radians(e.address_latitude)))) AS distanceFromUsersPostcode
FROM event e
INNER JOIN event_organiser eo on e.event_organiser_id = eo.id
WHERE (e.event_name LIKE in_search OR e.address_town LIKE in_search OR e.address_county LIKE in_search OR eo.event_organiser_name LIKE in_search)
AND e.start_date_time >= in_start_date
AND e.start_date_time <= in_end_date
AND e.enabled = true
HAVING distanceFromUsersPostcode < in_maxDistanceFromUser
/*
* 1 *
ORDER BY distanceFromUsersPostcode
* 2 *
ORDER BY IF(in_orderBy='LOCATION', CAST(distanceFromUsersPostcode AS DECIMAL), e.start_date_time) ASC;
*/
ORDER BY
CASE in_orderBy
WHEN 'LOCATION' THEN distanceFromUsersPostcode
ELSE e.start_date_time
END
ASC;
END
Now the issue is this, the current uncommented ORDER BY clause seems to treat the DECIMAL value distanceFromUsersPostcode as a VARCHAR (or string) value.
It orders results in the form :
0.4, 101.9, 102.8, 11.1, 11.9
The same can be said if I used the variant labelled as * 2 *
However, if I revert to the original variant labelled as * 1 *, the results would be ordered as expected :
0.4, 11.1, 11.9, 101.9, 102.8
My guessing was that MySQL treated the distanceFromUsersPostcode variable as a VARCHAR if used inside the IF Function (* 2 *), hence my attempt to cast it to a DECIMAL. This however, has no effect.
Can anybody shed any light on what is happening here?
The following acts as expected but isn't very elegant of course as it duplicates the entire query :
BEGIN
IF in_orderBy='LOCATION' THEN
SELECT DISTINCT e.*, ( 3959 * acos( cos( radians(in_latitude) ) * cos( radians( e.address_latitude ) )
* cos( radians(e.address_longitude) - radians(in_longitude)) + sin(radians(in_latitude))
* sin( radians(e.address_latitude)))) AS distanceFromUsersPostcode
FROM event e
INNER JOIN event_organiser eo on e.event_organiser_id = eo.id
WHERE (e.event_name LIKE in_search OR e.address_town LIKE in_search OR e.address_county LIKE in_search OR eo.event_organiser_name LIKE in_search)
AND e.start_date_time >= in_start_date
AND e.start_date_time <= in_end_date
AND e.enabled = true
HAVING distanceFromUsersPostcode < in_maxDistanceFromUser
ORDER BY distanceFromUsersPostcode;
ELSE
SELECT DISTINCT e.*, ( 3959 * acos( cos( radians(in_latitude) ) * cos( radians( e.address_latitude ) )
* cos( radians(e.address_longitude) - radians(in_longitude)) + sin(radians(in_latitude))
* sin( radians(e.address_latitude)))) AS distanceFromUsersPostcode
FROM event e
INNER JOIN event_organiser eo on e.event_organiser_id = eo.id
WHERE (e.event_name LIKE in_search OR e.address_town LIKE in_search OR e.address_county LIKE in_search OR eo.event_organiser_name LIKE in_search)
AND e.start_date_time >= in_start_date
AND e.start_date_time <= in_end_date
AND e.enabled = true
HAVING distanceFromUsersPostcode < in_maxDistanceFromUser
ORDER BY e.start_date_time;
END IF;
END
Because you use different data types for sorting mysql will try to convert them to something convenient to be able to compare the values. In your particular case it will be conversion to string. So it makes no sense to convert distanceFromUsersPostcode to a number, because it will be converted back to string. You will need to transform the string in a convenient format for number sorting. LPAD function will help you here.
ORDER BY
CASE in_orderBy
WHEN 'LOCATION' THEN LPAD(CAST(distanceFromUsersPostcode as CHAR),6)
ELSE e.start_date_time
END
ASC;
I have following mysql query:
SELECT a.id
, ( 3959 * acos( cos( radians(a.latitude) ) * cos( radians( w.latitude ) )
* cos( radians( w.longitude ) - radians(-a.longitude) )
+ sin( radians(a.latitude) ) * sin(radians(w.latitude)) ) ) distance
FROM global_restaurants a
JOIN webgeocities w
ON w.name = a.locality
AND w.country_code = a.country
AND a.latitude LIKE w.latitude
JOIN states s
ON s.state_code = w.state_code
AND w.country_code = s.country_code
WHERE a.city_id = 0
After execution it returns:
id distance
70 6665.478678743614
70 6496.46971480875
70 6725.900646648246
70 6733.5156930808
90 6969.449661399672
90 7252.889875588891
I want it to only return 2 rows with minimum distance like:
id distance
70 6496.46971480875
90 6969.449661399672
add order by at the end of your query:
order by distance ASC LIMIT 2;
now you calculating the distance but
at end of the query you must sort them and limit the returns row count..
.
NOTE:
i think you have a small mistake on your query
the 3959 is for miles but i think the values are in meter
your full query can be like this:
select a.id, ( 6371000 * acos( cos( radians(a.latitude) ) * cos( radians( w.latitude ) )
* cos( radians( w.longitude ) - radians(-a.longitude) ) + sin( radians(a.latitude) ) * sin(radians(w.latitude)) ) ) AS distance
from `global_restaurants` as a INNER JOIN webgeocities as w ON (w.name = a.locality AND w.country_code = a.country and a.latitude like w.latitude) INNER JOIN
states AS s ON (s.state_code = w.state_code and w.country_code = s.country_code) where a.city_id = '0' ORDER BY distance ASC LIMIT 2;
For simplicity, let's assumed that you have your query in view - stack, then your query should look like this
select stack.* from stack join (
SELECT id, GROUP_CONCAT(distance ORDER BY
distance asc) grouped_distance FROM stack GROUP BY id
) group_distance on group_distance.id=stack.id
where find_in_set(stack.distance,group_distance.grouped_distance)between 1 and 2;
I'm using the Haversine formula with this query and it works until. The goal is to check if each row has certain categories in an value of $deflin which looks like $deflin = category1, category2, category3. The results will show rows within 50km and if it contains any of the categories defined by $deflin. Not sure how to approach this either with WHERE IN or LIKE. Any help is appreciated.
MySQL for query
$awaka = "SELECT *,
( 6371 * acos( cos( radians(?) ) * cos( radians(job_latitude) ) *
cos(radians(?) - radians(job_longitude) ) + sin( radians(?) ) *
sin( radians(job_latitude) ) ) ) AS distance FROM job, users
WHERE job.listee_id = users.user_id AND job.job_category LIKE ?
HAVING distance < 50";
$result = $this->db->query($awaka, array($conlat, $conlong, $conlat, $deflin));
I don't think you like to use the LIKE operator since it only searches for patterns within a column and you only wan't to return rows that really has one of your categories. Instead you should use an IN clause to check if a job has one of your categories:
// contains the id for each category
$categories = array(1, 4, 5, 6);
$deflin = implode(',', $categories);
// if job_category is a text column you could do like this instead
$categories = array('category1', 'category2', 'category3');
$deflin = implode(',', $categories);
$awaka = "SELECT *,
( 6371 * acos( cos( radians(?) ) * cos( radians(job_latitude) ) *
cos(radians(?) - radians(job_longitude) ) + sin( radians(?) ) *
sin( radians(job_latitude) ) ) ) AS distance FROM job, users
WHERE job.listee_id = users.user_id AND job.job_category IN ($deflin)
HAVING distance < 50";
I want to use UNION to join two SQL SELECT queries. I need the final data to use the HAVING clause to filter the entire query. Here is my statement:
SELECT CLIENT,
BIZNAME,
BIZSTREET,
BIZCITY,
BIZSTATE,
BIZZIP,
BIZPHONE,
URL,
LAT,
LNG,
CONSOLIDATED,
( 3959 * ACOS(COS(RADIANS('%s')) * COS(RADIANS(LAT)) * COS(
RADIANS(LNG) - RADIANS('%s'))
+ SIN
(RADIANS('%s')) * SIN(RADIANS(LAT))) ) AS distance
FROM BizInfo
INNER JOIN WebSites
ON WebSites.CUSTOMER = BizInfo.CUSTOMER
WHERE BizInfo.CLIENT = 'GCB'
AND WebSites.STATUS <> 'Cancel'
AND WebSites.STATUS <> 'In Progress'
AND WebSites.STATUS <> 'Review'
AND WebSites.STATUS <> 'Testing'
UNION
SELECT CLIENT,
BIZNAME,
BIZSTREET,
BIZCITY,
BIZSTATE,
BIZZIP,
BIZPHONE,
'http://www.abc-site.com',
LAT,
LNG,
'0',
( 3959 * ACOS(COS(RADIANS('%s')) * COS(RADIANS(LAT)) * COS(
RADIANS(LNG) - RADIANS('%s'))
+ SIN
(RADIANS('%s')) * SIN(RADIANS(LAT))) ) AS distance
FROM BizInfo
WHERE CLIENT = 'GCB'
AND BIZNAME = 'Acme'
HAVING DISTANCE < '%s'
ORDER BY DISTANCE
LIMIT 0, 200
I read on this site http://www.really-fine.com/SQL_union.html (GROUP BY and HAVING clauses can be used only within individual queries and cannot be used to affect the final results set. ), but I don't understand how to implement this or if it is correct.
How do I properly write this SQL query?
You can probably solve that very easily by wrapping everything into a subquery. Something like :
SELECT
*
FROM
(
SELECT Client, BizName, BizStreet, BizCity, BizState, BizZip, BizPhone, url, lat, lng, Consolidated,
( 3959 * acos( cos( radians('%s') ) * cos( radians( lat ) ) * cos( radians( lng ) - radians('%s') ) + sin( radians('%s') ) * sin( radians( lat ) ) ) )
AS distance FROM BizInfo
INNER JOIN WebSites ON WebSites.Customer = BizInfo.Customer
WHERE BizInfo.Client = 'GCB'
AND WebSites.Status <> 'Cancel' AND WebSites.Status <> 'In Progress' AND WebSites.Status <> 'Review' AND WebSites.Status <> 'Testing'
UNION SELECT Client, BizName, BizStreet, BizCity, BizState, BizZip, BizPhone, 'http://www.abc-site.com', lat, lng, '0',
( 3959 * acos( cos( radians('%s') ) * cos( radians( lat ) ) * cos( radians( lng ) - radians('%s') ) + sin( radians('%s') ) * sin( radians( lat ) ) ) )
AS distance FROM BizInfo WHERE Client = 'GCB' AND BizName = 'Acme'
) AS ClientInfo
WHERE
distance < '%s'
ORDER BY
distance
LIMIT 0 , 200
This is the fastest path to what you want but it is not very clean.
Also please tell me that all those parameters are not vulnerable to SQL injection...?
HAVING can only be used in an aggregate query with a GROUP BY clause. Your queries do not. Use WHERE instead.
Those are scalar functions, not aggregate functions. So to filter on the result, use the WHERE clause, not the HAVING clause. You are filtering horizontally, not vertically.
You cannot use a column alias in the WHERE clause, so you have to restate the functions used in the SELECT list (I've done that in the query below).
Try the following. There are 2 issues at play.
SELECT Client,
BizName,
BizStreet,
BizCity,
BizState,
BizZip,
BizPhone,
url,
lat,
lng,
Consolidated,
(3959 * acos(cos(radians('%s')) * cos(radians(lat)) *
cos(radians(lng) - radians('%s')) +
sin(radians('%s')) * sin(radians(lat)))) AS distance
FROM BizInfo
INNER JOIN WebSites
ON WebSites.Customer = BizInfo.Customer
WHERE BizInfo.Client = 'GCB'
AND WebSites.Status <> 'Cancel'
AND WebSites.Status <> 'In Progress'
AND WebSites.Status <> 'Review'
AND WebSites.Status <> 'Testing'
UNION
SELECT Client,
BizName,
BizStreet,
BizCity,
BizState,
BizZip,
BizPhone,
'http://www.abc-site.com',
lat,
lng,
'0',
(3959 * acos(cos(radians('%s')) * cos(radians(lat)) *
cos(radians(lng) - radians('%s')) +
sin(radians('%s')) * sin(radians(lat)))) AS distance
FROM BizInfo
WHERE Client = 'GCB'
AND BizName = 'Acme'
and (3959 * acos(cos(radians('%s')) * cos(radians(lat)) *
cos(radians(lng) - radians('%s')) +
sin(radians('%s')) * sin(radians(lat)))) < '%s'
ORDER BY distance LIMIT 0, 200