Converting 'WITH CTE' MSSQL to MySQL - mysql

I realize that this doesn't directly translate from MSSQL to MySQL but I'm not not sure how to make it work. Any help that you have is appreciated.
;WITH cte As (
SELECT
post_id,
status,
dealer,
distributor,
SUM(
3959 * acos(
cos( radians(%f) ) *
cos( radians( lat ) ) *
cos( radians( lng ) - radians(%f) ) +
sin( radians(%f) ) *
sin( radians( lat ) )
)
)
AS DISTANCE
FROM wp_geodatastore
GROUP BY post_id, status, dealer, distributor
)
SELECT post_id, status, dealer, distributor, DISTANCE
FROM cte WHERE (DISTANCE < %d)
AND status = 'publish' AND dealer = 'on' AND distributor = 'on'
ORDER BY DISTANCE
OFFSET %d ROWS
FETCH NEXT %d ROWS ONLY;

Just make it a subquery:
SELECT post_id, status, dealer, distributor, DISTANCE
FROM (SELECT post_id, status, dealer, distributor,
SUM( 3959 * acos(
cos( radians(%f) ) *
cos( radians( lat ) ) *
cos( radians( lng ) - radians(%f) ) +
sin( radians(%f) ) *
sin( radians( lat ) )
)
) AS DISTANCE
FROM wp_geodatastore
GROUP BY post_id, status, dealer, distributor
) cte
WHERE (DISTANCE < %d) AND
status = 'publish' AND dealer = 'on' AND distributor = 'on'
ORDER BY DISTANCE
OFFSET %d ROWS
FETCH NEXT %d ROWS ONLY;

Related

MySQL Query grouping and counting results

here's my current query - it shows cities and a distance value:
SELECT city, ( 6371 * acos( cos( radians(51.228482) ) * cos( radians( geo_breit
) ) * cos( radians( geo_lang ) - radians(6.772457) ) + sin( radians(51.228482) ) * sin( radians( geo_breit
) ) ) )
AS distance
FROM partner HAVING distance < 50
ORDER BY city
which outputs:
i'm totally lost in grouping the data so it would return the following
city total max_distance
-------------------------------------------------------------
Bedburg 2 29.445788818447983
Bergheim 2 32.83341311371813
Bergisch Gladbach 4 39.62206615477636
Bochum 1 43.15766028349356
...
EDIT: i added the actual query, the problem is that grouping/max doesn't work when using the HAVING keyword .. any ideas?
Either you use a nested query like this:
SELECT cities.city,
count(*) as total,
max(cities.distance) as max_distance
FROM (SELECT city,
( 6371 * acos( cos( radians(51.228482) ) * cos( radians( geo_breit ) ) * cos( radians( geo_lang ) - radians(6.772457) ) + sin( radians(51.228482) ) * sin( radians( geo_breit) ) ) ) AS distance
FROM partner HAVING distance < 50
ORDER BY city) as cities
GROUP BY cities.city
Or you could try it with a where clause instead of a having
SELECT city, count(*),
max( 6371 * acos( cos( radians(51.228482) ) * cos( radians( geo_breit ) ) * cos( radians( geo_lang ) - radians(6.772457) ) + sin( radians(51.228482) ) * sin( radians( geo_breit) ) ) ) AS max_distance
FROM partner
WHERE (6371 * acos( cos( radians(51.228482) ) * cos( radians( geo_breit ) ) * cos( radians( geo_lang ) - radians(6.772457) ) + sin( radians(51.228482) ) * sin( radians( geo_breit) ) ) < 50
ORDER BY city
GROUP BY city
SELECT city, COUNT(city) as total, MAX(distance) AS max_distance
FROM table
GROUP BY city
You can use COUNT function count the number of occurrences and MAX function to find the maximum distance value with GROUP BY
Query
select city, count(city) as total, max(distance) as max_distance
from your_table_name
group by city;
use GROUP BY
SELECT city, COUNT(city) total, MAX(distance) max_distance
FROM table
GROUP BY city
The GROUP BY statement is used in conjunction with the aggregate functions to group the result-set by one or more columns.

Merging 2 sql queries into a single one

I have two sql queries which when run independent produces the correct results
Query 1
SELECT id,
(6371 * acos( cos( radians(9.977364864079215) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians(76.58620953448485) ) + sin( radians(9.977364864079215) ) * sin( radians( latitude ) ) ) )
AS distance
FROM geodata HAVING distance < 20
ORDER BY distance
LIMIT 0 , 20;
Query 2
SELECT DISTINCT e.* FROM schools e
WHERE (
(e.type = 'preprimary')
)
AND(
e.title LIKE '%government%'
)
LIMIT 0, 10
I want to merge the first query with the second one, so that it should return all "preprimary" type schools with title like "government" located within 20KM radius and the result needs to be ordered by the distance.
How can I merge the two queries? I tried using JOINING the geodata table on the school table. But I dont know the remaining. Sorry, if this is a silly question. I am pretty new to SQL world.
SELECT DISTINCT school.* FROM
( SELECT geodata.id,
(6371 * acos( cos( radians(9.977364864079215) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians(76.58620953448485) ) + sin( radians(9.977364864079215) ) * sin( radians( latitude ) ) ) )
AS distance ,school.*
FROM geodata LEFT JOIN school on geodata.id=school.id
WHERE
(school.type = 'preprimary')
AND(
school.title LIKE '%government%'
)
AND school.id IS NOT NULL
HAVING distance < 20 )x
ORDER BY x.distance
LIMIT 0 , 10;
Try this:
SELECT *
From (
SELECT DISTINCT e.* ,
(6371 * acos( cos( radians(9.977364864079215) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians(76.58620953448485) ) + sin( radians(9.977364864079215) ) * sin( radians( latitude ) ) )
) as distance
FROM schools e
LEFT JOIN geodata g ON e.id=g.id
WHERE (e.type = 'preprimary')
AND ( e.title LIKE '%government%' )
) as s
Where s.distance < 20
Order by s.distance

combine 2 select queries in mysql

I have 2 select statements:
timestamp of emp getting awards for specific emp id
SELECT * FROM user_table,employeetable,awards where user_table.empid=employeetable.empid AND user_table.empid=awards.empid AND user_table.empid=123 ORDER BY timestamp DESC
All employees staying around 25 miles from the current loc:current location: lat =37 lng=-122
SELECT * ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) )+ sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM user_table,employeetable,awards where user_table.empid=employeetable.empid AND user_table.empid=awards.empid HAVING distance < 25 ORDER BY distance;
How do I combine both and ORDER BY timestamp ?btw both have field timestamp.
1.has specific user
2.all users within specific radius
I really appreciate any help.Thanks in Advance.
You can combine the two queries into a single query, just using logic in the where clause (which this has turned into a having clause:
select *, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) )+ sin( radians(37) ) * sin( radians( lat ) ) ) ) as distance
from user u join
employee e
on u.empid = e.empid join
awards a
on u.empid = a.empid
having empid = 123 or distance < 25;
This uses having instead of where so the distance column alias can be used instead of the formula.

How to improve this huge SQL query?

I have a big SQL query (for MySQL) that is slow. It's a union of two select statements. I have tried different things, but any slight variance gives me a different result set from the original. Any help with improving it will be greatly appreciated. Thanks. Here is the SQL:
(SELECT
CONCAT(city_name,', ',region) value,
latitude,
longitude,
id,
population,
( 3959 * acos( cos( radians($latitude) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians($longitude) ) + sin( radians($latitude) ) * sin( radians( latitude ) ) ) )
AS distance,
CASE region
WHEN '$region' THEN 1
ELSE 0
END AS region_match
FROM `cities`
$where and foo_count > 5
ORDER BY region_match desc, foo_count desc
limit 0, 11)
UNION
(SELECT
CONCAT(city_name,', ',region) value,
latitude,
longitude,
id,
population,
( 3959 * acos( cos( radians($latitude) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians($longitude) ) + sin( radians($latitude) ) * sin( radians( latitude ) ) ) )
AS distance,
CASE region
WHEN '$region' THEN 1
ELSE 0
END AS region_match
FROM `cities`
$where
ORDER BY region_match desc, population desc, distance asc
limit 0, 11)
limit 0, 11
The SQL does take some interpolated values (prefixed with the dollar sign($)).
The following might give the same result (I'm not sure about how the maximum/minimum functions are called in SQL, but you should get an idea -- you need two fields derived from foo_count which separate the items of the first part of your UNION from those of the second one and allow ordering within the first part without disturbing the order in the second part) -- of course, you later need a second query to throw the additional fields out again:
SELECT
CONCAT(city_name,', ',region) value,
latitude,
longitude,
id,
population,
( 3959 * acos( cos( radians($latitude) ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians($longitude) ) + sin( radians($latitude) ) * sin( radians( latitude ) ) ) )
AS distance,
min ( 6, max (foo_count, 5)) AS group_discriminator,
max ( 6, foo_count) AS rank_for_use_in_first_group,
CASE region
WHEN '$region' THEN 1
ELSE 0
END AS region_match
FROM `cities`
$where
ORDER BY group_discriminator desc, region_match desc, rank_for_use_in_first_group desc, population desc, distance asc
limit 0, 11
EDIT: Improvements

Mysql, select value of one cell depending on contents of another cell

My current code
SELECT post_id, ( 3959 * ACOS( COS( RADIANS( 34.09 ) ) * COS( RADIANS( lat ) ) * COS( RADIANS( lng ) - RADIANS( -117.55 ) ) + SIN( RADIANS( 34.09 ) ) * SIN( RADIANS( lat ) ) ) ) AS distance FROM wp_postmeta WHERE `meta_key` LIKE '%location_l%' HAVING distance < 2500 ORDER BY distance LIMIT 0 , 20
Here are two sample row of data (meta_key is not always long or lat):
post_id = 123
meta_key = location_longitude
meta_value = -119.890000
post_id = 123
meta_key = location_latitude
meta_value = 42.170000
How do I modify my query to replace 'lat' and 'lng' in my original query to be the contents of the meta_value listed above? Something like this?
select meta_value where meta_key = location_latitude
If you GROUP BY post_id, you should be able to use MAX() aggregates in place of lat,lng, surrounding CASE statements which determine whether the current row is latitude or longitude. The others will be NULL, and therefore eliminated by the aggregate.
I think this ought to work so you won't need any subselects.
SELECT
post_id,
( 3959 * ACOS( COS( RADIANS( 34.09 ) ) * COS( RADIANS( MAX(CASE WHEN meta_key='location_latitude' THEN meta_value ELSE NULL END) ) ) * COS( RADIANS( MAX(CASE WHEN meta_key='location_longitude' THEN meta_value ELSE NULL END) ) - RADIANS( -117.55 ) ) + SIN( RADIANS( 34.09 ) ) * SIN( RADIANS( MAX(CASE WHEN meta_key='location_latitude' THEN meta_value ELSE NULL END) ) ) ) ) AS distance
FROM wp_postmeta
WHERE `meta_key` IN ('location_latitude','location_longitude')
GROUP BY post_id
HAVING distance < 2500
ORDER BY distance
LIMIT 0 , 20
The moving parts here are:
MAX(CASE WHEN meta_key='location_latitude' THEN meta_value ELSE NULL END)
This translates as: If this row's meta_key is 'location_latitude', return the meta_value, otherwise return NULL. We expect then that since two rows are returned for the post_id (lat,lng), the MAX() value returned above is always the non-null one -- the correct latitude or longitude value from meta_value.
You need to join the table to itself (this is common in databases with EAV structure):
SELECT
post_id,
distance
FROM
( SELECT
lng.post_id,
( 3959 * ACOS( COS( RADIANS( 34.09 ) )
* COS( RADIANS( lat.meta_value ) )
* COS( RADIANS( lng.meta_value )
- RADIANS( -117.55 ) )
+ SIN( RADIANS( 34.09 ) )
* SIN( RADIANS( lat.meta_value ) )
)
) AS distance
FROM wp_postmeta AS lng
JOIN wp_postmeta AS lat
ON lat.post_id = lng.post_id
WHERE lng.meta_key = 'location_longitude'
AND lat.meta_key = 'location_latitude'
) AS tmp
WHERE distance < 2500
ORDER BY distance
LIMIT 0 , 20 ;
One approach is to replace the reference to the table (i.e. FROM wp_postmeta) in your query with an inline view, something like this:
FROM
( SELECT plat.post_id
, plat.meta_value AS lat
, plng.meta_value AS lng
FROM wp_postmeta plat
JOIN wp_postmeta plng
ON plat.post_id = plng.post_id
AND plat.meta_key = 'location_latitude'
AND plng.meta_ley = 'location_longitude'
) wpm
(NOTE: this assumes that there is only one row for each post_id for each of the two meta_key values of interest...)
This uses an "inline view" to combine the latitude and longitude values for each post_id into a single row. (You can run just the query for the inline view to confirm that its returning the results you expect.) We can use an inline view in place of a table reference, in more recent versions of MySQL (version >= 5.0).
SELECT a.post_id,
( 3959 * ACOS( COS( RADIANS( 34.09 ) ) * COS( RADIANS( lat.meta_value ) ) * COS( RADIANS( lng.meta_value ) - RADIANS( -117.55 ) ) + SIN( RADIANS( 34.09 ) ) * SIN( RADIANS( lat.meta_value ) ) ) ) AS distance
FROM wp_postmeta lat, wp_postmeta lng
WHERE lat.post_id = lng.postID
AND lat.meta_key = 'location_latitude'
AND lng.meta_key = 'location_longitude'
HAVING distance < 2500
ORDER BY distance LIMIT 0 , 20